mirror of
https://github.com/uutils/diffutils.git
synced 2026-06-10 15:48:59 -07:00
Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9db1eab1d0 | |||
| 2392acfad1 | |||
| 2a899a9fc7 | |||
| 6ec8370b4b | |||
| dbabf399d5 | |||
| b815162b80 | |||
| 12b205e655 | |||
| 8a6504dd83 | |||
| 67ef43083a | |||
| b1738538a8 | |||
| eea6b62b20 | |||
| f08a3bf512 | |||
| e55ee893dd | |||
| 24245ee098 | |||
| 11f815a7c2 | |||
| e9a8141618 | |||
| c9a756eb43 | |||
| 468c4bf934 | |||
| 1e8fdd58d9 | |||
| e98b5e179e | |||
| 1901982375 | |||
| eee6f49920 | |||
| 8a3a977d2c | |||
| fa4e0c6097 | |||
| d362046ae5 | |||
| 7964afa336 | |||
| 80b993141b | |||
| d922313c8c | |||
| 3e246ab36c | |||
| 4b70969ff1 | |||
| 767c6f6c4a | |||
| 1f896ca1ac | |||
| 713bd210ab | |||
| 61314eaf4e | |||
| bf9147733d | |||
| ce8457cbdb | |||
| df778c610b | |||
| d92132e721 | |||
| 99d4d02985 | |||
| e7dc6558c6 | |||
| 8c6a648aef | |||
| 0304391bc5 | |||
| 8de0ca60d1 | |||
| 43b9c524d9 | |||
| 3dc3fdf5cd | |||
| b7261a43f4 | |||
| 37fe1ae808 | |||
| 22d973fce6 | |||
| fe28610f21 | |||
| 3a8eddfe2c | |||
| 476e69ee20 | |||
| 65993d6a13 | |||
| 39d2ece187 | |||
| 46a26e896b | |||
| 14799eea89 | |||
| 831348d1fc | |||
| 00a5c0ba44 | |||
| bf104648c1 | |||
| 5669f164b3 | |||
| 11bf271666 | |||
| 674974d5e6 | |||
| 2ba35db431 | |||
| fcec7277c9 | |||
| b8efad6b90 | |||
| 68e2f51983 | |||
| 4edaee190f | |||
| 7f7821f558 | |||
| 1149a247dd | |||
| 1b311c6673 | |||
| aedd0684d1 | |||
| 54c02bdf0b | |||
| ba7cb0aef9 | |||
| 33783d094e | |||
| 900e1c3a68 | |||
| 0a77fe12b9 | |||
| 86bd05c739 | |||
| 00e18a6b0c | |||
| f6eb0835b0 | |||
| be66ff3299 | |||
| e1c319f96b | |||
| 84ad116845 | |||
| 6dc34fed44 | |||
| 9507ca28d7 | |||
| c325291696 | |||
| c08e0b6e1f | |||
| 72da7fca40 | |||
| 61fb0657c1 | |||
| 096aa1dad9 | |||
| 2d9e625a5b | |||
| d863fe443a | |||
| 6be94d8683 | |||
| 44ef772e4a | |||
| bbfca84e17 | |||
| a3a372ff36 | |||
| 5b814f8530 | |||
| 88a7568b52 | |||
| 80c9944bf7 | |||
| 043c5f9493 | |||
| 9ff8f89626 | |||
| 42eb15b87a |
+21
-137
@@ -28,6 +28,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: install GNU patch on MacOS
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
run: brew install gpatch
|
||||||
|
- name: set up PATH on Windows
|
||||||
|
# Needed to use GNU's patch.exe instead of Strawberry Perl patch
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: echo "C:\Program Files\Git\usr\bin" >> $env:GITHUB_PATH
|
||||||
- run: cargo test
|
- run: cargo test
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
@@ -69,88 +76,6 @@ jobs:
|
|||||||
path: tests/test-results.json
|
path: tests/test-results.json
|
||||||
- run: ./tests/print-test-results.sh tests/test-results.json
|
- run: ./tests/print-test-results.sh tests/test-results.json
|
||||||
|
|
||||||
build:
|
|
||||||
name: build ${{ matrix.binary || 'findutils' }} ${{ matrix.target }}
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
container: ${{ fromJson(matrix.container || '{"image":null}') }}
|
|
||||||
timeout-minutes: 30
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-20.04
|
|
||||||
target: x86_64-unknown-linux-musl
|
|
||||||
- os: ubuntu-20.04
|
|
||||||
target: aarch64-unknown-linux-musl
|
|
||||||
- os: ubuntu-20.04
|
|
||||||
target: armv7-unknown-linux-musleabi
|
|
||||||
container: '{"image": "messense/rust-musl-cross:armv7-musleabi"}'
|
|
||||||
- os: ubuntu-20.04
|
|
||||||
target: i686-unknown-linux-musl
|
|
||||||
container: '{"image": "messense/rust-musl-cross:i686-musl"}'
|
|
||||||
- os: macOS-11
|
|
||||||
target: x86_64-apple-darwin
|
|
||||||
macosx_deployment_target: 10.13
|
|
||||||
developer_dir: /Applications/Xcode_11.7.app
|
|
||||||
sdkroot: /Applications/Xcode_11.7.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk
|
|
||||||
- os: macos-14
|
|
||||||
target: aarch64-apple-darwin
|
|
||||||
macosx_deployment_target: 11.0
|
|
||||||
- os: windows-2019
|
|
||||||
target: x86_64-pc-windows-msvc
|
|
||||||
rustflags: -Ctarget-feature=+crt-static
|
|
||||||
steps:
|
|
||||||
- name: Clone repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install rust
|
|
||||||
uses: ./.github/actions/rust-toolchain
|
|
||||||
with:
|
|
||||||
toolchain: ${{ matrix.target == 'aarch64-apple-darwin' && 'beta' || 'stable' }}
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
if: ${{ !matrix.container }}
|
|
||||||
|
|
||||||
- name: Install musl-tools (x86_64)
|
|
||||||
run: sudo apt-get install musl-tools
|
|
||||||
if: ${{ matrix.target == 'x86_64-unknown-linux-musl' }}
|
|
||||||
|
|
||||||
- name: Install musl-tools (arm64)
|
|
||||||
run: |
|
|
||||||
set -x
|
|
||||||
sed 's/mirror+file:\/etc\/apt\/apt-mirrors\.txt/[arch=arm64] http:\/\/ports.ubuntu.com\/ubuntu-ports\//g' /etc/apt/sources.list | sudo tee /etc/apt/sources.list.d/ports.list
|
|
||||||
sudo dpkg --add-architecture arm64
|
|
||||||
sudo apt-get update || true
|
|
||||||
sudo apt-get install musl-dev:arm64 binutils-multiarch gcc-10-aarch64-linux-gnu libc6-dev-arm64-cross
|
|
||||||
apt-get download musl-tools:arm64
|
|
||||||
sudo dpkg-deb -x musl-tools_*_arm64.deb /
|
|
||||||
sed 2iREALGCC=aarch64-linux-gnu-gcc-10 /usr/bin/musl-gcc | sudo tee /usr/bin/aarch64-linux-musl-gcc > /dev/null
|
|
||||||
sudo chmod +x /usr/bin/aarch64-linux-musl-gcc
|
|
||||||
if: ${{ matrix.target == 'aarch64-unknown-linux-musl' }}
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: cargo build --locked --release --bin ${{ matrix.binary || 'findutils' }} --target ${{ matrix.target }} --features=openssl/vendored ${{ matrix.extra_args }}
|
|
||||||
env:
|
|
||||||
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER: aarch64-linux-musl-gcc
|
|
||||||
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
|
|
||||||
DEVELOPER_DIR: ${{ matrix.developer_dir }}
|
|
||||||
SDKROOT: ${{ matrix.sdkroot }}
|
|
||||||
RUSTFLAGS: ${{ matrix.rustflags }}
|
|
||||||
|
|
||||||
# Workaround for the lack of substring() function in github actions expressions.
|
|
||||||
- name: Id
|
|
||||||
id: id
|
|
||||||
shell: bash
|
|
||||||
run: echo "id=${ID#refs/tags/}" >> $GITHUB_OUTPUT
|
|
||||||
env:
|
|
||||||
ID: ${{ startsWith(github.ref, 'refs/tags/') && github.ref || github.sha }}
|
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: ${{ matrix.binary || 'findutils' }}-${{ steps.id.outputs.id }}-${{ matrix.target }}
|
|
||||||
path: target/${{ matrix.target }}/release/${{ matrix.binary || 'findutils' }}${{ endsWith(matrix.target, '-msvc') && '.exe' || '' }}
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
name: Code Coverage
|
name: Code Coverage
|
||||||
runs-on: ${{ matrix.job.os }}
|
runs-on: ${{ matrix.job.os }}
|
||||||
@@ -185,13 +110,22 @@ jobs:
|
|||||||
|
|
||||||
- name: rust toolchain ~ install
|
- name: rust toolchain ~ install
|
||||||
uses: dtolnay/rust-toolchain@nightly
|
uses: dtolnay/rust-toolchain@nightly
|
||||||
|
- run: rustup component add llvm-tools-preview
|
||||||
|
- name: install GNU patch on MacOS
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
run: brew install gpatch
|
||||||
|
- name: set up PATH on Windows
|
||||||
|
# Needed to use GNU's patch.exe instead of Strawberry Perl patch
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: echo "C:\Program Files\Git\usr\bin" >> $env:GITHUB_PATH
|
||||||
- name: Test
|
- name: Test
|
||||||
run: cargo test ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast
|
run: cargo test ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast
|
||||||
env:
|
env:
|
||||||
CARGO_INCREMENTAL: "0"
|
CARGO_INCREMENTAL: "0"
|
||||||
RUSTC_WRAPPER: ""
|
RUSTC_WRAPPER: ""
|
||||||
RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
|
RUSTFLAGS: "-Cinstrument-coverage -Zcoverage-options=branch -Ccodegen-units=1 -Copt-level=0 -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
|
||||||
RUSTDOCFLAGS: "-Cpanic=abort"
|
RUSTDOCFLAGS: "-Cpanic=abort"
|
||||||
|
LLVM_PROFILE_FILE: "diffutils-%p-%m.profraw"
|
||||||
- name: "`grcov` ~ install"
|
- name: "`grcov` ~ install"
|
||||||
id: build_grcov
|
id: build_grcov
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -219,67 +153,17 @@ jobs:
|
|||||||
COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info"
|
COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info"
|
||||||
mkdir -p "${COVERAGE_REPORT_DIR}"
|
mkdir -p "${COVERAGE_REPORT_DIR}"
|
||||||
# display coverage files
|
# display coverage files
|
||||||
grcov . --output-type files --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" | sort --unique
|
grcov . --output-type files --binary-path "${COVERAGE_REPORT_DIR}" | sort --unique
|
||||||
# generate coverage report
|
# generate coverage report
|
||||||
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"
|
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --binary-path "${COVERAGE_REPORT_DIR}" --branch
|
||||||
echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT
|
echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT
|
||||||
- name: Upload coverage results (to Codecov.io)
|
- name: Upload coverage results (to Codecov.io)
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v4
|
||||||
# if: steps.vars.outputs.HAS_CODECOV_TOKEN
|
|
||||||
with:
|
with:
|
||||||
# token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
file: ${{ steps.coverage.outputs.report }}
|
file: ${{ steps.coverage.outputs.report }}
|
||||||
## flags: IntegrationTests, UnitTests, ${{ steps.vars.outputs.CODECOV_FLAGS }}
|
## flags: IntegrationTests, UnitTests, ${{ steps.vars.outputs.CODECOV_FLAGS }}
|
||||||
flags: ${{ steps.vars.outputs.CODECOV_FLAGS }}
|
flags: ${{ steps.vars.outputs.CODECOV_FLAGS }}
|
||||||
name: codecov-umbrella
|
name: codecov-umbrella
|
||||||
fail_ci_if_error: false
|
fail_ci_if_error: false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
release:
|
|
||||||
name: release
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [build, clippy, check, test]
|
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
|
||||||
steps:
|
|
||||||
- name: Clone repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Check versions
|
|
||||||
run: |
|
|
||||||
tag_name=${GITHUB_REF#refs/tags/}
|
|
||||||
v=$(grep -m 1 "^version" Cargo.toml|sed -e "s|version = \"\(.*\)\"|\1|")
|
|
||||||
if ! echo $tag_name|grep -q $v; then
|
|
||||||
echo "Mistmatch of the version:"
|
|
||||||
echo "Cargo.toml says $v while the tag is $tag_name"
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Get artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
|
|
||||||
- name: Create release assets
|
|
||||||
run: |
|
|
||||||
for d in findutils-*; do
|
|
||||||
chmod +x "$d/findutils"*
|
|
||||||
cp README.md LICENSE "$d/"
|
|
||||||
tar -zcvf "$d.tar.gz" "$d"
|
|
||||||
echo -n "$(shasum -ba 256 "$d.tar.gz" | cut -d " " -f 1)" > "$d.tar.gz.sha256"
|
|
||||||
if [[ $d =~ (findutils-)(.*)?(x86_64-pc-windows)(.*)? ]]; then
|
|
||||||
zip -r "$d.zip" "$d"
|
|
||||||
echo -n "$(shasum -ba 256 "$d.zip" | cut -d " " -f 1)" > "$d.zip.sha256"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Create release
|
|
||||||
run: |
|
|
||||||
sudo apt-get update && sudo apt-get install -y hub
|
|
||||||
tag_name=${GITHUB_REF#refs/tags/}
|
|
||||||
for f in findutils-*.tar.gz* findutils-*.zip*; do
|
|
||||||
if [[ -f "$f" ]]; then
|
|
||||||
files="$files -a $f";
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
hub release create -m $tag_name $tag_name $files
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|||||||
@@ -0,0 +1,271 @@
|
|||||||
|
# Copyright 2022-2024, axodotdev
|
||||||
|
# SPDX-License-Identifier: MIT or Apache-2.0
|
||||||
|
#
|
||||||
|
# CI that:
|
||||||
|
#
|
||||||
|
# * checks for a Git Tag that looks like a release
|
||||||
|
# * builds artifacts with cargo-dist (archives, installers, hashes)
|
||||||
|
# * uploads those artifacts to temporary workflow zip
|
||||||
|
# * on success, uploads the artifacts to a GitHub Release
|
||||||
|
#
|
||||||
|
# Note that the GitHub Release will be created with a generated
|
||||||
|
# title/body based on your changelogs.
|
||||||
|
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
# This task will run whenever you push a git tag that looks like a version
|
||||||
|
# like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc.
|
||||||
|
# Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where
|
||||||
|
# PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION
|
||||||
|
# must be a Cargo-style SemVer Version (must have at least major.minor.patch).
|
||||||
|
#
|
||||||
|
# If PACKAGE_NAME is specified, then the announcement will be for that
|
||||||
|
# package (erroring out if it doesn't have the given version or isn't cargo-dist-able).
|
||||||
|
#
|
||||||
|
# If PACKAGE_NAME isn't specified, then the announcement will be for all
|
||||||
|
# (cargo-dist-able) packages in the workspace with that version (this mode is
|
||||||
|
# intended for workspaces with only one dist-able package, or with all dist-able
|
||||||
|
# packages versioned/released in lockstep).
|
||||||
|
#
|
||||||
|
# If you push multiple tags at once, separate instances of this workflow will
|
||||||
|
# spin up, creating an independent announcement for each one. However, GitHub
|
||||||
|
# will hard limit this to 3 tags per commit, as it will assume more tags is a
|
||||||
|
# mistake.
|
||||||
|
#
|
||||||
|
# If there's a prerelease-style suffix to the version, then the release(s)
|
||||||
|
# will be marked as a prerelease.
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '**[0-9]+.[0-9]+.[0-9]+*'
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Run 'cargo dist plan' (or host) to determine what tasks we need to do
|
||||||
|
plan:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
val: ${{ steps.plan.outputs.manifest }}
|
||||||
|
tag: ${{ !github.event.pull_request && github.ref_name || '' }}
|
||||||
|
tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }}
|
||||||
|
publishing: ${{ !github.event.pull_request }}
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: Install cargo-dist
|
||||||
|
# we specify bash to get pipefail; it guards against the `curl` command
|
||||||
|
# failing. otherwise `sh` won't catch that `curl` returned non-0
|
||||||
|
shell: bash
|
||||||
|
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.13.3/cargo-dist-installer.sh | sh"
|
||||||
|
# sure would be cool if github gave us proper conditionals...
|
||||||
|
# so here's a doubly-nested ternary-via-truthiness to try to provide the best possible
|
||||||
|
# functionality based on whether this is a pull_request, and whether it's from a fork.
|
||||||
|
# (PRs run on the *source* but secrets are usually on the *target* -- that's *good*
|
||||||
|
# but also really annoying to build CI around when it needs secrets to work right.)
|
||||||
|
- id: plan
|
||||||
|
run: |
|
||||||
|
cargo dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json
|
||||||
|
echo "cargo dist ran successfully"
|
||||||
|
cat plan-dist-manifest.json
|
||||||
|
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: "Upload dist-manifest.json"
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: artifacts-plan-dist-manifest
|
||||||
|
path: plan-dist-manifest.json
|
||||||
|
|
||||||
|
# Build and packages all the platform-specific things
|
||||||
|
build-local-artifacts:
|
||||||
|
name: build-local-artifacts (${{ join(matrix.targets, ', ') }})
|
||||||
|
# Let the initial task tell us to not run (currently very blunt)
|
||||||
|
needs:
|
||||||
|
- plan
|
||||||
|
if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
# Target platforms/runners are computed by cargo-dist in create-release.
|
||||||
|
# Each member of the matrix has the following arguments:
|
||||||
|
#
|
||||||
|
# - runner: the github runner
|
||||||
|
# - dist-args: cli flags to pass to cargo dist
|
||||||
|
# - install-dist: expression to run to install cargo-dist on the runner
|
||||||
|
#
|
||||||
|
# Typically there will be:
|
||||||
|
# - 1 "global" task that builds universal installers
|
||||||
|
# - N "local" tasks that build each platform's binaries and platform-specific installers
|
||||||
|
matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }}
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json
|
||||||
|
steps:
|
||||||
|
- name: enable windows longpaths
|
||||||
|
run: |
|
||||||
|
git config --global core.longpaths true
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- uses: swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
key: ${{ join(matrix.targets, '-') }}
|
||||||
|
- name: Install cargo-dist
|
||||||
|
run: ${{ matrix.install_dist }}
|
||||||
|
# Get the dist-manifest
|
||||||
|
- name: Fetch local artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
pattern: artifacts-*
|
||||||
|
path: target/distrib/
|
||||||
|
merge-multiple: true
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
${{ matrix.packages_install }}
|
||||||
|
- name: Build artifacts
|
||||||
|
run: |
|
||||||
|
# Actually do builds and make zips and whatnot
|
||||||
|
cargo dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json
|
||||||
|
echo "cargo dist ran successfully"
|
||||||
|
- id: cargo-dist
|
||||||
|
name: Post-build
|
||||||
|
# We force bash here just because github makes it really hard to get values up
|
||||||
|
# to "real" actions without writing to env-vars, and writing to env-vars has
|
||||||
|
# inconsistent syntax between shell and powershell.
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# Parse out what we just built and upload it to scratch storage
|
||||||
|
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
|
||||||
|
jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT"
|
||||||
|
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||||
|
- name: "Upload artifacts"
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: artifacts-build-local-${{ join(matrix.targets, '_') }}
|
||||||
|
path: |
|
||||||
|
${{ steps.cargo-dist.outputs.paths }}
|
||||||
|
${{ env.BUILD_MANIFEST_NAME }}
|
||||||
|
|
||||||
|
# Build and package all the platform-agnostic(ish) things
|
||||||
|
build-global-artifacts:
|
||||||
|
needs:
|
||||||
|
- plan
|
||||||
|
- build-local-artifacts
|
||||||
|
runs-on: "ubuntu-20.04"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: Install cargo-dist
|
||||||
|
shell: bash
|
||||||
|
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.13.3/cargo-dist-installer.sh | sh"
|
||||||
|
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
|
||||||
|
- name: Fetch local artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
pattern: artifacts-*
|
||||||
|
path: target/distrib/
|
||||||
|
merge-multiple: true
|
||||||
|
- id: cargo-dist
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cargo dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
|
||||||
|
echo "cargo dist ran successfully"
|
||||||
|
|
||||||
|
# Parse out what we just built and upload it to scratch storage
|
||||||
|
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
|
||||||
|
jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT"
|
||||||
|
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||||
|
- name: "Upload artifacts"
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: artifacts-build-global
|
||||||
|
path: |
|
||||||
|
${{ steps.cargo-dist.outputs.paths }}
|
||||||
|
${{ env.BUILD_MANIFEST_NAME }}
|
||||||
|
# Determines if we should publish/announce
|
||||||
|
host:
|
||||||
|
needs:
|
||||||
|
- plan
|
||||||
|
- build-local-artifacts
|
||||||
|
- build-global-artifacts
|
||||||
|
# Only run if we're "publishing", and only if local and global didn't fail (skipped is fine)
|
||||||
|
if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }}
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
runs-on: "ubuntu-20.04"
|
||||||
|
outputs:
|
||||||
|
val: ${{ steps.host.outputs.manifest }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: Install cargo-dist
|
||||||
|
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.13.3/cargo-dist-installer.sh | sh"
|
||||||
|
# Fetch artifacts from scratch-storage
|
||||||
|
- name: Fetch artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
pattern: artifacts-*
|
||||||
|
path: target/distrib/
|
||||||
|
merge-multiple: true
|
||||||
|
# This is a harmless no-op for GitHub Releases, hosting for that happens in "announce"
|
||||||
|
- id: host
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cargo dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
|
||||||
|
echo "artifacts uploaded and released successfully"
|
||||||
|
cat dist-manifest.json
|
||||||
|
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: "Upload dist-manifest.json"
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
# Overwrite the previous copy
|
||||||
|
name: artifacts-dist-manifest
|
||||||
|
path: dist-manifest.json
|
||||||
|
|
||||||
|
# Create a GitHub Release while uploading all files to it
|
||||||
|
announce:
|
||||||
|
needs:
|
||||||
|
- plan
|
||||||
|
- host
|
||||||
|
# use "always() && ..." to allow us to wait for all publish jobs while
|
||||||
|
# still allowing individual publish jobs to skip themselves (for prereleases).
|
||||||
|
# "host" however must run to completion, no skipping allowed!
|
||||||
|
if: ${{ always() && needs.host.result == 'success' }}
|
||||||
|
runs-on: "ubuntu-20.04"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: "Download GitHub Artifacts"
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
pattern: artifacts-*
|
||||||
|
path: artifacts
|
||||||
|
merge-multiple: true
|
||||||
|
- name: Cleanup
|
||||||
|
run: |
|
||||||
|
# Remove the granular manifests
|
||||||
|
rm -f artifacts/*-dist-manifest.json
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
tag: ${{ needs.plan.outputs.tag }}
|
||||||
|
name: ${{ fromJson(needs.host.outputs.val).announcement_title }}
|
||||||
|
body: ${{ fromJson(needs.host.outputs.val).announcement_github_body }}
|
||||||
|
prerelease: ${{ fromJson(needs.host.outputs.val).announcement_is_prerelease }}
|
||||||
|
artifacts: "artifacts/*"
|
||||||
Generated
+205
-32
@@ -11,6 +11,21 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android-tzdata"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
@@ -19,13 +34,14 @@ checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "assert_cmd"
|
name = "assert_cmd"
|
||||||
version = "2.0.14"
|
version = "2.0.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8"
|
checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"bstr",
|
"bstr",
|
||||||
"doc-comment",
|
"doc-comment",
|
||||||
|
"libc",
|
||||||
"predicates",
|
"predicates",
|
||||||
"predicates-core",
|
"predicates-core",
|
||||||
"predicates-tree",
|
"predicates-tree",
|
||||||
@@ -55,12 +71,44 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.15.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.90"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
||||||
|
dependencies = [
|
||||||
|
"android-tzdata",
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-traits",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diff"
|
name = "diff"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
@@ -75,9 +123,10 @@ checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diffutils"
|
name = "diffutils"
|
||||||
version = "0.3.0"
|
version = "0.4.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
|
"chrono",
|
||||||
"diff",
|
"diff",
|
||||||
"predicates",
|
"predicates",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
@@ -100,7 +149,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -118,6 +167,38 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.60"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.153"
|
version = "0.2.153"
|
||||||
@@ -130,6 +211,12 @@ version = "0.4.13"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.1"
|
version = "2.7.1"
|
||||||
@@ -152,10 +239,16 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "predicates"
|
name = "once_cell"
|
||||||
version = "3.1.0"
|
version = "1.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8"
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "predicates"
|
||||||
|
version = "3.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"difflib",
|
"difflib",
|
||||||
@@ -211,9 +304,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.3"
|
version = "1.10.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -248,7 +341,7 @@ dependencies = [
|
|||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -293,14 +386,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.10.0"
|
version = "3.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67"
|
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -317,9 +411,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.11"
|
version = "0.1.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wait-timeout"
|
name = "wait-timeout"
|
||||||
@@ -330,6 +424,60 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
@@ -361,6 +509,15 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
@@ -371,14 +528,24 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-sys"
|
||||||
version = "0.52.2"
|
version = "0.59.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d98532992affa02e52709d5b4d145a3668ae10d9081eea4a7f26f719a8476f71"
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc",
|
||||||
"windows_i686_gnu",
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
"windows_i686_msvc",
|
"windows_i686_msvc",
|
||||||
"windows_x86_64_gnu",
|
"windows_x86_64_gnu",
|
||||||
"windows_x86_64_gnullvm",
|
"windows_x86_64_gnullvm",
|
||||||
@@ -387,45 +554,51 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.1"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e7269c1442e75af9fa59290383f7665b828efc76c429cc0b7f2ecb33cf51ebae"
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.1"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f70ab2cebf332b7ecbdd98900c2da5298a8c862472fb35c75fc297eabb9d89b8"
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.1"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "679f235acf6b1639408c0f6db295697a19d103b0cdc88146aa1b992c580c647d"
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.1"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3480ac194b55ae274a7e135c21645656825da4a7f5b6e9286291b2113c94a78b"
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.1"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42c46bab241c121402d1cb47d028ea3680ee2f359dcc287482dcf7fdddc73363"
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.1"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc885a4332ee1afb9a1bacf11514801011725570d35675abc229ce7e3afe4d20"
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.1"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e440c60457f84b0bee09208e62acc7ade264b38c4453f6312b8c9ab1613e73c"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yansi"
|
name = "yansi"
|
||||||
|
|||||||
+25
-6
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "diffutils"
|
name = "diffutils"
|
||||||
version = "0.3.0"
|
version = "0.4.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "A CLI app for generating diff files"
|
description = "A CLI app for generating diff files"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
@@ -15,13 +15,32 @@ name = "diffutils"
|
|||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
diff = "0.1.10"
|
chrono = "0.4.38"
|
||||||
regex = "1.10.3"
|
diff = "0.1.13"
|
||||||
|
regex = "1.10.4"
|
||||||
same-file = "1.0.6"
|
same-file = "1.0.6"
|
||||||
unicode-width = "0.1.11"
|
unicode-width = "0.1.12"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1"
|
pretty_assertions = "1.4.0"
|
||||||
assert_cmd = "2.0.14"
|
assert_cmd = "2.0.14"
|
||||||
predicates = "3.1.0"
|
predicates = "3.1.0"
|
||||||
tempfile = "3.10.0"
|
tempfile = "3.10.1"
|
||||||
|
|
||||||
|
# The profile that 'cargo dist' will build with
|
||||||
|
[profile.dist]
|
||||||
|
inherits = "release"
|
||||||
|
lto = "thin"
|
||||||
|
|
||||||
|
# Config for 'cargo dist'
|
||||||
|
[workspace.metadata.dist]
|
||||||
|
# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax)
|
||||||
|
cargo-dist-version = "0.13.3"
|
||||||
|
# CI backends to support
|
||||||
|
ci = ["github"]
|
||||||
|
# The installers to generate for each app
|
||||||
|
installers = []
|
||||||
|
# Target platforms to build apps for (Rust target-triple syntax)
|
||||||
|
targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
|
||||||
|
# Publish jobs to run in CI
|
||||||
|
pr-run-mode = "plan"
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
Copyright (c) Michael Howell
|
||||||
|
Copyright (c) uutils developers
|
||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
http://www.apache.org/licenses/
|
http://www.apache.org/licenses/
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
Copyright (c) Michael Howell
|
||||||
|
Copyright (c) uutils developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any
|
Permission is hereby granted, free of charge, to any
|
||||||
person obtaining a copy of this software and associated
|
person obtaining a copy of this software and associated
|
||||||
documentation files (the "Software"), to deal in the
|
documentation files (the "Software"), to deal in the
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ edition = "2018"
|
|||||||
cargo-fuzz = true
|
cargo-fuzz = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libfuzzer-sys = "0.4"
|
libfuzzer-sys = "0.4.7"
|
||||||
diffutils = { path = "../" }
|
diffutils = { path = "../" }
|
||||||
|
|
||||||
# Prevent this from interfering with workspaces
|
# Prevent this from interfering with workspaces
|
||||||
|
|||||||
+36
-27
@@ -8,6 +8,7 @@ use std::io::Write;
|
|||||||
|
|
||||||
use crate::params::Params;
|
use crate::params::Params;
|
||||||
use crate::utils::do_write_line;
|
use crate::utils::do_write_line;
|
||||||
|
use crate::utils::get_modification_time;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum DiffLine {
|
pub enum DiffLine {
|
||||||
@@ -267,10 +268,14 @@ fn make_diff(
|
|||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn diff(expected: &[u8], actual: &[u8], params: &Params) -> Vec<u8> {
|
pub fn diff(expected: &[u8], actual: &[u8], params: &Params) -> Vec<u8> {
|
||||||
|
let from_modified_time = get_modification_time(¶ms.from.to_string_lossy());
|
||||||
|
let to_modified_time = get_modification_time(¶ms.to.to_string_lossy());
|
||||||
let mut output = format!(
|
let mut output = format!(
|
||||||
"*** {0}\t\n--- {1}\t\n",
|
"*** {0}\t{1}\n--- {2}\t{3}\n",
|
||||||
params.from.to_string_lossy(),
|
params.from.to_string_lossy(),
|
||||||
params.to.to_string_lossy()
|
from_modified_time,
|
||||||
|
params.to.to_string_lossy(),
|
||||||
|
to_modified_time
|
||||||
)
|
)
|
||||||
.into_bytes();
|
.into_bytes();
|
||||||
let diff_results = make_diff(expected, actual, params.context_count, params.brief);
|
let diff_results = make_diff(expected, actual, params.context_count, params.brief);
|
||||||
@@ -434,26 +439,26 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
File::create(&format!("{target}/ab.diff"))
|
File::create(format!("{target}/ab.diff"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create(&format!("{target}/alef")).unwrap();
|
let mut fa = File::create(format!("{target}/alef")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/bet")).unwrap();
|
let mut fb = File::create(format!("{target}/bet")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
let output = Command::new("patch")
|
let output = Command::new("patch")
|
||||||
.arg("-p0")
|
.arg("-p0")
|
||||||
.arg("--context")
|
.arg("--context")
|
||||||
.stdin(File::open(&format!("{target}/ab.diff")).unwrap())
|
.stdin(File::open(format!("{target}/ab.diff")).unwrap())
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(output.status.success(), "{output:?}");
|
assert!(output.status.success(), "{output:?}");
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
let alef = fs::read(&format!("{target}/alef")).unwrap();
|
let alef = fs::read(format!("{target}/alef")).unwrap();
|
||||||
assert_eq!(alef, bet);
|
assert_eq!(alef, bet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -515,26 +520,26 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
File::create(&format!("{target}/ab_.diff"))
|
File::create(format!("{target}/ab_.diff"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create(&format!("{target}/alef_")).unwrap();
|
let mut fa = File::create(format!("{target}/alef_")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/bet_")).unwrap();
|
let mut fb = File::create(format!("{target}/bet_")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
let output = Command::new("patch")
|
let output = Command::new("patch")
|
||||||
.arg("-p0")
|
.arg("-p0")
|
||||||
.arg("--context")
|
.arg("--context")
|
||||||
.stdin(File::open(&format!("{target}/ab_.diff")).unwrap())
|
.stdin(File::open(format!("{target}/ab_.diff")).unwrap())
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(output.status.success(), "{output:?}");
|
assert!(output.status.success(), "{output:?}");
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
let alef = fs::read(&format!("{target}/alef_")).unwrap();
|
let alef = fs::read(format!("{target}/alef_")).unwrap();
|
||||||
assert_eq!(alef, bet);
|
assert_eq!(alef, bet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -599,26 +604,26 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
File::create(&format!("{target}/abx.diff"))
|
File::create(format!("{target}/abx.diff"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create(&format!("{target}/alefx")).unwrap();
|
let mut fa = File::create(format!("{target}/alefx")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/betx")).unwrap();
|
let mut fb = File::create(format!("{target}/betx")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
let output = Command::new("patch")
|
let output = Command::new("patch")
|
||||||
.arg("-p0")
|
.arg("-p0")
|
||||||
.arg("--context")
|
.arg("--context")
|
||||||
.stdin(File::open(&format!("{target}/abx.diff")).unwrap())
|
.stdin(File::open(format!("{target}/abx.diff")).unwrap())
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(output.status.success(), "{output:?}");
|
assert!(output.status.success(), "{output:?}");
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
let alef = fs::read(&format!("{target}/alefx")).unwrap();
|
let alef = fs::read(format!("{target}/alefx")).unwrap();
|
||||||
assert_eq!(alef, bet);
|
assert_eq!(alef, bet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -686,26 +691,26 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
File::create(&format!("{target}/abr.diff"))
|
File::create(format!("{target}/abr.diff"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create(&format!("{target}/alefr")).unwrap();
|
let mut fa = File::create(format!("{target}/alefr")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/betr")).unwrap();
|
let mut fb = File::create(format!("{target}/betr")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
let output = Command::new("patch")
|
let output = Command::new("patch")
|
||||||
.arg("-p0")
|
.arg("-p0")
|
||||||
.arg("--context")
|
.arg("--context")
|
||||||
.stdin(File::open(&format!("{target}/abr.diff")).unwrap())
|
.stdin(File::open(format!("{target}/abr.diff")).unwrap())
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(output.status.success(), "{output:?}");
|
assert!(output.status.success(), "{output:?}");
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
let alef = fs::read(&format!("{target}/alefr")).unwrap();
|
let alef = fs::read(format!("{target}/alefr")).unwrap();
|
||||||
assert_eq!(alef, bet);
|
assert_eq!(alef, bet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -717,6 +722,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stop_early() {
|
fn test_stop_early() {
|
||||||
|
use crate::assert_diff_eq;
|
||||||
|
|
||||||
let from_filename = "foo";
|
let from_filename = "foo";
|
||||||
let from = ["a", "b", "c", ""].join("\n");
|
let from = ["a", "b", "c", ""].join("\n");
|
||||||
let to_filename = "bar";
|
let to_filename = "bar";
|
||||||
@@ -731,9 +738,10 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let expected_full = [
|
let expected_full = [
|
||||||
"*** foo\t",
|
"*** foo\tTIMESTAMP",
|
||||||
"--- bar\t",
|
"--- bar\tTIMESTAMP",
|
||||||
"***************",
|
"***************",
|
||||||
"*** 1,3 ****",
|
"*** 1,3 ****",
|
||||||
" a",
|
" a",
|
||||||
@@ -746,7 +754,7 @@ mod tests {
|
|||||||
"",
|
"",
|
||||||
]
|
]
|
||||||
.join("\n");
|
.join("\n");
|
||||||
assert_eq!(diff_full, expected_full.as_bytes());
|
assert_diff_eq!(diff_full, expected_full);
|
||||||
|
|
||||||
let diff_brief = diff(
|
let diff_brief = diff(
|
||||||
from.as_bytes(),
|
from.as_bytes(),
|
||||||
@@ -758,8 +766,9 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let expected_brief = ["*** foo\t", "--- bar\t", ""].join("\n");
|
|
||||||
assert_eq!(diff_brief, expected_brief.as_bytes());
|
let expected_brief = ["*** foo\tTIMESTAMP", "--- bar\tTIMESTAMP", ""].join("\n");
|
||||||
|
assert_diff_eq!(diff_brief, expected_brief);
|
||||||
|
|
||||||
let nodiff_full = diff(
|
let nodiff_full = diff(
|
||||||
from.as_bytes(),
|
from.as_bytes(),
|
||||||
|
|||||||
+55
-46
@@ -188,9 +188,8 @@ mod tests {
|
|||||||
for &d in &[0, 1, 2] {
|
for &d in &[0, 1, 2] {
|
||||||
for &e in &[0, 1, 2] {
|
for &e in &[0, 1, 2] {
|
||||||
for &f in &[0, 1, 2] {
|
for &f in &[0, 1, 2] {
|
||||||
use std::fs::{self, File};
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::process::Command;
|
|
||||||
let mut alef = Vec::new();
|
let mut alef = Vec::new();
|
||||||
let mut bet = Vec::new();
|
let mut bet = Vec::new();
|
||||||
alef.write_all(if a == 0 { b"a\n" } else { b"b\n" })
|
alef.write_all(if a == 0 { b"a\n" } else { b"b\n" })
|
||||||
@@ -226,26 +225,30 @@ mod tests {
|
|||||||
// This test diff is intentionally reversed.
|
// This test diff is intentionally reversed.
|
||||||
// We want it to turn the alef into bet.
|
// We want it to turn the alef into bet.
|
||||||
let diff = diff_w(&alef, &bet, &format!("{target}/alef")).unwrap();
|
let diff = diff_w(&alef, &bet, &format!("{target}/alef")).unwrap();
|
||||||
File::create("target/ab.ed")
|
File::create(format!("{target}/ab.ed"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create(&format!("{target}/alef")).unwrap();
|
let mut fa = File::create(format!("{target}/alef")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/bet")).unwrap();
|
let mut fb = File::create(format!("{target}/bet")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
let output = Command::new("ed")
|
#[cfg(not(windows))] // there's no ed on windows
|
||||||
.arg(&format!("{target}/alef"))
|
{
|
||||||
.stdin(File::open("target/ab.ed").unwrap())
|
use std::process::Command;
|
||||||
.output()
|
let output = Command::new("ed")
|
||||||
.unwrap();
|
.arg(format!("{target}/alef"))
|
||||||
assert!(output.status.success(), "{output:?}");
|
.stdin(File::open(format!("{target}/ab.ed")).unwrap())
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
.output()
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
.unwrap();
|
||||||
let alef = fs::read(&format!("{target}/alef")).unwrap();
|
assert!(output.status.success(), "{output:?}");
|
||||||
assert_eq!(alef, bet);
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
|
let alef = std::fs::read(format!("{target}/alef")).unwrap();
|
||||||
|
assert_eq!(alef, bet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -265,9 +268,8 @@ mod tests {
|
|||||||
for &d in &[0, 1, 2] {
|
for &d in &[0, 1, 2] {
|
||||||
for &e in &[0, 1, 2] {
|
for &e in &[0, 1, 2] {
|
||||||
for &f in &[0, 1, 2] {
|
for &f in &[0, 1, 2] {
|
||||||
use std::fs::{self, File};
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::process::Command;
|
|
||||||
let mut alef = Vec::new();
|
let mut alef = Vec::new();
|
||||||
let mut bet = Vec::new();
|
let mut bet = Vec::new();
|
||||||
alef.write_all(if a == 0 { b"\n" } else { b"b\n" }).unwrap();
|
alef.write_all(if a == 0 { b"\n" } else { b"b\n" }).unwrap();
|
||||||
@@ -296,27 +298,31 @@ mod tests {
|
|||||||
}
|
}
|
||||||
// This test diff is intentionally reversed.
|
// This test diff is intentionally reversed.
|
||||||
// We want it to turn the alef into bet.
|
// We want it to turn the alef into bet.
|
||||||
let diff = diff_w(&alef, &bet, "target/alef_").unwrap();
|
let diff = diff_w(&alef, &bet, &format!("{target}/alef_")).unwrap();
|
||||||
File::create("target/ab_.ed")
|
File::create(format!("{target}/ab_.ed"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create("target/alef_").unwrap();
|
let mut fa = File::create(format!("{target}/alef_")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/bet_")).unwrap();
|
let mut fb = File::create(format!("{target}/bet_")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
let output = Command::new("ed")
|
#[cfg(not(windows))] // there's no ed on windows
|
||||||
.arg("target/alef_")
|
{
|
||||||
.stdin(File::open("target/ab_.ed").unwrap())
|
use std::process::Command;
|
||||||
.output()
|
let output = Command::new("ed")
|
||||||
.unwrap();
|
.arg(format!("{target}/alef_"))
|
||||||
assert!(output.status.success(), "{output:?}");
|
.stdin(File::open(format!("{target}/ab_.ed")).unwrap())
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
.output()
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
.unwrap();
|
||||||
let alef = fs::read("target/alef_").unwrap();
|
assert!(output.status.success(), "{output:?}");
|
||||||
assert_eq!(alef, bet);
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
|
let alef = std::fs::read(format!("{target}/alef_")).unwrap();
|
||||||
|
assert_eq!(alef, bet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,9 +342,8 @@ mod tests {
|
|||||||
for &d in &[0, 1, 2] {
|
for &d in &[0, 1, 2] {
|
||||||
for &e in &[0, 1, 2] {
|
for &e in &[0, 1, 2] {
|
||||||
for &f in &[0, 1, 2] {
|
for &f in &[0, 1, 2] {
|
||||||
use std::fs::{self, File};
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::process::Command;
|
|
||||||
let mut alef = Vec::new();
|
let mut alef = Vec::new();
|
||||||
let mut bet = Vec::new();
|
let mut bet = Vec::new();
|
||||||
alef.write_all(if a == 0 { b"a\n" } else { b"f\n" })
|
alef.write_all(if a == 0 { b"a\n" } else { b"f\n" })
|
||||||
@@ -374,26 +379,30 @@ mod tests {
|
|||||||
// This test diff is intentionally reversed.
|
// This test diff is intentionally reversed.
|
||||||
// We want it to turn the alef into bet.
|
// We want it to turn the alef into bet.
|
||||||
let diff = diff_w(&alef, &bet, &format!("{target}/alefr")).unwrap();
|
let diff = diff_w(&alef, &bet, &format!("{target}/alefr")).unwrap();
|
||||||
File::create("target/abr.ed")
|
File::create(format!("{target}/abr.ed"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create(&format!("{target}/alefr")).unwrap();
|
let mut fa = File::create(format!("{target}/alefr")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/betr")).unwrap();
|
let mut fb = File::create(format!("{target}/betr")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
let output = Command::new("ed")
|
#[cfg(not(windows))] // there's no ed on windows
|
||||||
.arg(&format!("{target}/alefr"))
|
{
|
||||||
.stdin(File::open("target/abr.ed").unwrap())
|
use std::process::Command;
|
||||||
.output()
|
let output = Command::new("ed")
|
||||||
.unwrap();
|
.arg(format!("{target}/alefr"))
|
||||||
assert!(output.status.success(), "{output:?}");
|
.stdin(File::open(format!("{target}/abr.ed")).unwrap())
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
.output()
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
.unwrap();
|
||||||
let alef = fs::read(&format!("{target}/alefr")).unwrap();
|
assert!(output.status.success(), "{output:?}");
|
||||||
assert_eq!(alef, bet);
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
|
let alef = std::fs::read(format!("{target}/alefr")).unwrap();
|
||||||
|
assert_eq!(alef, bet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pub mod context_diff;
|
pub mod context_diff;
|
||||||
pub mod ed_diff;
|
pub mod ed_diff;
|
||||||
|
pub mod macros;
|
||||||
pub mod normal_diff;
|
pub mod normal_diff;
|
||||||
pub mod params;
|
pub mod params;
|
||||||
pub mod unified_diff;
|
pub mod unified_diff;
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// asserts equality of the actual diff and expected diff
|
||||||
|
// considering datetime varitations
|
||||||
|
//
|
||||||
|
// It replaces the modification time in the actual diff
|
||||||
|
// with placeholder "TIMESTAMP" and then asserts the equality
|
||||||
|
//
|
||||||
|
// For eg.
|
||||||
|
// let brief = "*** fruits_old.txt\t2024-03-24 23:43:05.189597645 +0530\n
|
||||||
|
// --- fruits_new.txt\t2024-03-24 23:35:08.922581904 +0530\n";
|
||||||
|
//
|
||||||
|
// replaced = "*** fruits_old.txt\tTIMESTAMP\n
|
||||||
|
// --- fruits_new.txt\tTIMESTAMP\n";
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! assert_diff_eq {
|
||||||
|
($actual:expr, $expected:expr) => {{
|
||||||
|
use regex::Regex;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
let diff = str::from_utf8(&$actual).unwrap();
|
||||||
|
let re = Regex::new(r"\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ [+-]\d{4}").unwrap();
|
||||||
|
let actual = re.replacen(diff, 2, "TIMESTAMP");
|
||||||
|
|
||||||
|
assert_eq!(actual, $expected);
|
||||||
|
}};
|
||||||
|
}
|
||||||
+45
-9
@@ -4,19 +4,37 @@
|
|||||||
// files that was distributed with this source code.
|
// files that was distributed with this source code.
|
||||||
|
|
||||||
use crate::params::{parse_params, Format};
|
use crate::params::{parse_params, Format};
|
||||||
|
use regex::Regex;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::ffi::OsString;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::process::{exit, ExitCode};
|
use std::process::{exit, ExitCode};
|
||||||
|
|
||||||
mod context_diff;
|
mod context_diff;
|
||||||
mod ed_diff;
|
mod ed_diff;
|
||||||
|
mod macros;
|
||||||
mod normal_diff;
|
mod normal_diff;
|
||||||
mod params;
|
mod params;
|
||||||
mod unified_diff;
|
mod unified_diff;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
fn report_failure_to_read_input_file(
|
||||||
|
executable: &OsString,
|
||||||
|
filepath: &OsString,
|
||||||
|
error: &std::io::Error,
|
||||||
|
) {
|
||||||
|
// std::io::Error's display trait outputs "{detail} (os error {code})"
|
||||||
|
// but we want only the {detail} (error string) part
|
||||||
|
let error_code_re = Regex::new(r"\ \(os\ error\ \d+\)$").unwrap();
|
||||||
|
eprintln!(
|
||||||
|
"{}: {}: {}",
|
||||||
|
executable.to_string_lossy(),
|
||||||
|
filepath.to_string_lossy(),
|
||||||
|
error_code_re.replace(error.to_string().as_str(), ""),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Exit codes are documented at
|
// Exit codes are documented at
|
||||||
// https://www.gnu.org/software/diffutils/manual/html_node/Invoking-diff.html.
|
// https://www.gnu.org/software/diffutils/manual/html_node/Invoking-diff.html.
|
||||||
// An exit status of 0 means no differences were found,
|
// An exit status of 0 means no differences were found,
|
||||||
@@ -38,25 +56,43 @@ fn main() -> ExitCode {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if same_file::is_same_file(¶ms.from, ¶ms.to).unwrap_or(false) {
|
if params.from == "-" && params.to == "-"
|
||||||
|
|| same_file::is_same_file(¶ms.from, ¶ms.to).unwrap_or(false)
|
||||||
|
{
|
||||||
maybe_report_identical_files();
|
maybe_report_identical_files();
|
||||||
return ExitCode::SUCCESS;
|
return ExitCode::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read files
|
// read files
|
||||||
let from_content = match fs::read(¶ms.from) {
|
fn read_file_contents(filepath: &OsString) -> io::Result<Vec<u8>> {
|
||||||
|
if filepath == "-" {
|
||||||
|
let mut content = Vec::new();
|
||||||
|
io::stdin().read_to_end(&mut content).and(Ok(content))
|
||||||
|
} else {
|
||||||
|
fs::read(filepath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut io_error = false;
|
||||||
|
let from_content = match read_file_contents(¶ms.from) {
|
||||||
Ok(from_content) => from_content,
|
Ok(from_content) => from_content,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Failed to read from-file: {e}");
|
report_failure_to_read_input_file(¶ms.executable, ¶ms.from, &e);
|
||||||
return ExitCode::from(2);
|
io_error = true;
|
||||||
|
vec![]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let to_content = match fs::read(¶ms.to) {
|
let to_content = match read_file_contents(¶ms.to) {
|
||||||
Ok(to_content) => to_content,
|
Ok(to_content) => to_content,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Failed to read to-file: {e}");
|
report_failure_to_read_input_file(¶ms.executable, ¶ms.to, &e);
|
||||||
return ExitCode::from(2);
|
io_error = true;
|
||||||
|
vec![]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if io_error {
|
||||||
|
return ExitCode::from(2);
|
||||||
|
}
|
||||||
|
|
||||||
// run diff
|
// run diff
|
||||||
let result: Vec<u8> = match params.format {
|
let result: Vec<u8> = match params.format {
|
||||||
Format::Normal => normal_diff::diff(&from_content, &to_content, ¶ms),
|
Format::Normal => normal_diff::diff(&from_content, &to_content, ¶ms),
|
||||||
|
|||||||
+24
-24
@@ -275,26 +275,26 @@ mod tests {
|
|||||||
// This test diff is intentionally reversed.
|
// This test diff is intentionally reversed.
|
||||||
// We want it to turn the alef into bet.
|
// We want it to turn the alef into bet.
|
||||||
let diff = diff(&alef, &bet, &Params::default());
|
let diff = diff(&alef, &bet, &Params::default());
|
||||||
File::create(&format!("{target}/ab.diff"))
|
File::create(format!("{target}/ab.diff"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create(&format!("{target}/alef")).unwrap();
|
let mut fa = File::create(format!("{target}/alef")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/bet")).unwrap();
|
let mut fb = File::create(format!("{target}/bet")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
let output = Command::new("patch")
|
let output = Command::new("patch")
|
||||||
.arg("-p0")
|
.arg("-p0")
|
||||||
.arg(&format!("{target}/alef"))
|
.arg(format!("{target}/alef"))
|
||||||
.stdin(File::open(&format!("{target}/ab.diff")).unwrap())
|
.stdin(File::open(format!("{target}/ab.diff")).unwrap())
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(output.status.success(), "{output:?}");
|
assert!(output.status.success(), "{output:?}");
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
let alef = fs::read(&format!("{target}/alef")).unwrap();
|
let alef = fs::read(format!("{target}/alef")).unwrap();
|
||||||
assert_eq!(alef, bet);
|
assert_eq!(alef, bet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -367,27 +367,27 @@ mod tests {
|
|||||||
// This test diff is intentionally reversed.
|
// This test diff is intentionally reversed.
|
||||||
// We want it to turn the alef into bet.
|
// We want it to turn the alef into bet.
|
||||||
let diff = diff(&alef, &bet, &Params::default());
|
let diff = diff(&alef, &bet, &Params::default());
|
||||||
File::create(&format!("{target}/abn.diff"))
|
File::create(format!("{target}/abn.diff"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create(&format!("{target}/alefn")).unwrap();
|
let mut fa = File::create(format!("{target}/alefn")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/betn")).unwrap();
|
let mut fb = File::create(format!("{target}/betn")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
let output = Command::new("patch")
|
let output = Command::new("patch")
|
||||||
.arg("-p0")
|
.arg("-p0")
|
||||||
.arg("--normal")
|
.arg("--normal")
|
||||||
.arg(&format!("{target}/alefn"))
|
.arg(format!("{target}/alefn"))
|
||||||
.stdin(File::open(&format!("{target}/abn.diff")).unwrap())
|
.stdin(File::open(format!("{target}/abn.diff")).unwrap())
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(output.status.success(), "{output:?}");
|
assert!(output.status.success(), "{output:?}");
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
let alef = fs::read(&format!("{target}/alefn")).unwrap();
|
let alef = fs::read(format!("{target}/alefn")).unwrap();
|
||||||
assert_eq!(alef, bet);
|
assert_eq!(alef, bet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,26 +441,26 @@ mod tests {
|
|||||||
// This test diff is intentionally reversed.
|
// This test diff is intentionally reversed.
|
||||||
// We want it to turn the alef into bet.
|
// We want it to turn the alef into bet.
|
||||||
let diff = diff(&alef, &bet, &Params::default());
|
let diff = diff(&alef, &bet, &Params::default());
|
||||||
File::create(&format!("{target}/ab_.diff"))
|
File::create(format!("{target}/ab_.diff"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create(&format!("{target}/alef_")).unwrap();
|
let mut fa = File::create(format!("{target}/alef_")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/bet_")).unwrap();
|
let mut fb = File::create(format!("{target}/bet_")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
let output = Command::new("patch")
|
let output = Command::new("patch")
|
||||||
.arg("-p0")
|
.arg("-p0")
|
||||||
.arg(&format!("{target}/alef_"))
|
.arg(format!("{target}/alef_"))
|
||||||
.stdin(File::open(&format!("{target}/ab_.diff")).unwrap())
|
.stdin(File::open(format!("{target}/ab_.diff")).unwrap())
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(output.status.success(), "{output:?}");
|
assert!(output.status.success(), "{output:?}");
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
let alef = fs::read(&format!("{target}/alef_")).unwrap();
|
let alef = fs::read(format!("{target}/alef_")).unwrap();
|
||||||
assert_eq!(alef, bet);
|
assert_eq!(alef, bet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -519,26 +519,26 @@ mod tests {
|
|||||||
// This test diff is intentionally reversed.
|
// This test diff is intentionally reversed.
|
||||||
// We want it to turn the alef into bet.
|
// We want it to turn the alef into bet.
|
||||||
let diff = diff(&alef, &bet, &Params::default());
|
let diff = diff(&alef, &bet, &Params::default());
|
||||||
File::create(&format!("{target}/abr.diff"))
|
File::create(format!("{target}/abr.diff"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create(&format!("{target}/alefr")).unwrap();
|
let mut fa = File::create(format!("{target}/alefr")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/betr")).unwrap();
|
let mut fb = File::create(format!("{target}/betr")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
let output = Command::new("patch")
|
let output = Command::new("patch")
|
||||||
.arg("-p0")
|
.arg("-p0")
|
||||||
.arg(&format!("{target}/alefr"))
|
.arg(format!("{target}/alefr"))
|
||||||
.stdin(File::open(&format!("{target}/abr.diff")).unwrap())
|
.stdin(File::open(format!("{target}/abr.diff")).unwrap())
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(output.status.success(), "{output:?}");
|
assert!(output.status.success(), "{output:?}");
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
let alef = fs::read(&format!("{target}/alefr")).unwrap();
|
let alef = fs::read(format!("{target}/alefr")).unwrap();
|
||||||
assert_eq!(alef, bet);
|
assert_eq!(alef, bet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+383
-84
File diff suppressed because it is too large
Load Diff
+41
-32
@@ -8,6 +8,7 @@ use std::io::Write;
|
|||||||
|
|
||||||
use crate::params::Params;
|
use crate::params::Params;
|
||||||
use crate::utils::do_write_line;
|
use crate::utils::do_write_line;
|
||||||
|
use crate::utils::get_modification_time;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum DiffLine {
|
pub enum DiffLine {
|
||||||
@@ -238,10 +239,14 @@ fn make_diff(
|
|||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn diff(expected: &[u8], actual: &[u8], params: &Params) -> Vec<u8> {
|
pub fn diff(expected: &[u8], actual: &[u8], params: &Params) -> Vec<u8> {
|
||||||
|
let from_modified_time = get_modification_time(¶ms.from.to_string_lossy());
|
||||||
|
let to_modified_time = get_modification_time(¶ms.to.to_string_lossy());
|
||||||
let mut output = format!(
|
let mut output = format!(
|
||||||
"--- {0}\t\n+++ {1}\t\n",
|
"--- {0}\t{1}\n+++ {2}\t{3}\n",
|
||||||
params.from.to_string_lossy(),
|
params.from.to_string_lossy(),
|
||||||
params.to.to_string_lossy()
|
from_modified_time,
|
||||||
|
params.to.to_string_lossy(),
|
||||||
|
to_modified_time
|
||||||
)
|
)
|
||||||
.into_bytes();
|
.into_bytes();
|
||||||
let diff_results = make_diff(expected, actual, params.context_count, params.brief);
|
let diff_results = make_diff(expected, actual, params.context_count, params.brief);
|
||||||
@@ -461,13 +466,13 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
File::create(&format!("{target}/ab.diff"))
|
File::create(format!("{target}/ab.diff"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create(&format!("{target}/alef")).unwrap();
|
let mut fa = File::create(format!("{target}/alef")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/bet")).unwrap();
|
let mut fb = File::create(format!("{target}/bet")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
@@ -489,13 +494,13 @@ mod tests {
|
|||||||
|
|
||||||
let output = Command::new("patch")
|
let output = Command::new("patch")
|
||||||
.arg("-p0")
|
.arg("-p0")
|
||||||
.stdin(File::open(&format!("{target}/ab.diff")).unwrap())
|
.stdin(File::open(format!("{target}/ab.diff")).unwrap())
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
println!("{}", String::from_utf8_lossy(&output.stdout));
|
println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
println!("{}", String::from_utf8_lossy(&output.stderr));
|
println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
assert!(output.status.success(), "{output:?}");
|
assert!(output.status.success(), "{output:?}");
|
||||||
let alef = fs::read(&format!("{target}/alef")).unwrap();
|
let alef = fs::read(format!("{target}/alef")).unwrap();
|
||||||
assert_eq!(alef, bet);
|
assert_eq!(alef, bet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -577,25 +582,25 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
File::create(&format!("{target}/abn.diff"))
|
File::create(format!("{target}/abn.diff"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create(&format!("{target}/alefn")).unwrap();
|
let mut fa = File::create(format!("{target}/alefn")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/betn")).unwrap();
|
let mut fb = File::create(format!("{target}/betn")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
let output = Command::new("patch")
|
let output = Command::new("patch")
|
||||||
.arg("-p0")
|
.arg("-p0")
|
||||||
.stdin(File::open(&format!("{target}/abn.diff")).unwrap())
|
.stdin(File::open(format!("{target}/abn.diff")).unwrap())
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(output.status.success(), "{output:?}");
|
assert!(output.status.success(), "{output:?}");
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
let alef = fs::read(&format!("{target}/alefn")).unwrap();
|
let alef = fs::read(format!("{target}/alefn")).unwrap();
|
||||||
assert_eq!(alef, bet);
|
assert_eq!(alef, bet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -673,25 +678,25 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
File::create(&format!("{target}/ab_.diff"))
|
File::create(format!("{target}/ab_.diff"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create(&format!("{target}/alef_")).unwrap();
|
let mut fa = File::create(format!("{target}/alef_")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/bet_")).unwrap();
|
let mut fb = File::create(format!("{target}/bet_")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
let output = Command::new("patch")
|
let output = Command::new("patch")
|
||||||
.arg("-p0")
|
.arg("-p0")
|
||||||
.stdin(File::open(&format!("{target}/ab_.diff")).unwrap())
|
.stdin(File::open(format!("{target}/ab_.diff")).unwrap())
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(output.status.success(), "{output:?}");
|
assert!(output.status.success(), "{output:?}");
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
let alef = fs::read(&format!("{target}/alef_")).unwrap();
|
let alef = fs::read(format!("{target}/alef_")).unwrap();
|
||||||
assert_eq!(alef, bet);
|
assert_eq!(alef, bet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -754,25 +759,25 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
File::create(&format!("{target}/abx.diff"))
|
File::create(format!("{target}/abx.diff"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create(&format!("{target}/alefx")).unwrap();
|
let mut fa = File::create(format!("{target}/alefx")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/betx")).unwrap();
|
let mut fb = File::create(format!("{target}/betx")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
let output = Command::new("patch")
|
let output = Command::new("patch")
|
||||||
.arg("-p0")
|
.arg("-p0")
|
||||||
.stdin(File::open(&format!("{target}/abx.diff")).unwrap())
|
.stdin(File::open(format!("{target}/abx.diff")).unwrap())
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(output.status.success(), "{output:?}");
|
assert!(output.status.success(), "{output:?}");
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
let alef = fs::read(&format!("{target}/alefx")).unwrap();
|
let alef = fs::read(format!("{target}/alefx")).unwrap();
|
||||||
assert_eq!(alef, bet);
|
assert_eq!(alef, bet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -840,25 +845,25 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
File::create(&format!("{target}/abr.diff"))
|
File::create(format!("{target}/abr.diff"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&diff)
|
.write_all(&diff)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut fa = File::create(&format!("{target}/alefr")).unwrap();
|
let mut fa = File::create(format!("{target}/alefr")).unwrap();
|
||||||
fa.write_all(&alef[..]).unwrap();
|
fa.write_all(&alef[..]).unwrap();
|
||||||
let mut fb = File::create(&format!("{target}/betr")).unwrap();
|
let mut fb = File::create(format!("{target}/betr")).unwrap();
|
||||||
fb.write_all(&bet[..]).unwrap();
|
fb.write_all(&bet[..]).unwrap();
|
||||||
let _ = fa;
|
let _ = fa;
|
||||||
let _ = fb;
|
let _ = fb;
|
||||||
let output = Command::new("patch")
|
let output = Command::new("patch")
|
||||||
.arg("-p0")
|
.arg("-p0")
|
||||||
.stdin(File::open(&format!("{target}/abr.diff")).unwrap())
|
.stdin(File::open(format!("{target}/abr.diff")).unwrap())
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(output.status.success(), "{output:?}");
|
assert!(output.status.success(), "{output:?}");
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||||
let alef = fs::read(&format!("{target}/alefr")).unwrap();
|
let alef = fs::read(format!("{target}/alefr")).unwrap();
|
||||||
assert_eq!(alef, bet);
|
assert_eq!(alef, bet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -870,6 +875,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stop_early() {
|
fn test_stop_early() {
|
||||||
|
use crate::assert_diff_eq;
|
||||||
|
|
||||||
let from_filename = "foo";
|
let from_filename = "foo";
|
||||||
let from = ["a", "b", "c", ""].join("\n");
|
let from = ["a", "b", "c", ""].join("\n");
|
||||||
let to_filename = "bar";
|
let to_filename = "bar";
|
||||||
@@ -884,9 +891,10 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let expected_full = [
|
let expected_full = [
|
||||||
"--- foo\t",
|
"--- foo\tTIMESTAMP",
|
||||||
"+++ bar\t",
|
"+++ bar\tTIMESTAMP",
|
||||||
"@@ -1,3 +1,3 @@",
|
"@@ -1,3 +1,3 @@",
|
||||||
" a",
|
" a",
|
||||||
"-b",
|
"-b",
|
||||||
@@ -895,7 +903,7 @@ mod tests {
|
|||||||
"",
|
"",
|
||||||
]
|
]
|
||||||
.join("\n");
|
.join("\n");
|
||||||
assert_eq!(diff_full, expected_full.as_bytes());
|
assert_diff_eq!(diff_full, expected_full);
|
||||||
|
|
||||||
let diff_brief = diff(
|
let diff_brief = diff(
|
||||||
from.as_bytes(),
|
from.as_bytes(),
|
||||||
@@ -907,8 +915,9 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let expected_brief = ["--- foo\t", "+++ bar\t", ""].join("\n");
|
|
||||||
assert_eq!(diff_brief, expected_brief.as_bytes());
|
let expected_brief = ["--- foo\tTIMESTAMP", "+++ bar\tTIMESTAMP", ""].join("\n");
|
||||||
|
assert_diff_eq!(diff_brief, expected_brief);
|
||||||
|
|
||||||
let nodiff_full = diff(
|
let nodiff_full = diff(
|
||||||
from.as_bytes(),
|
from.as_bytes(),
|
||||||
|
|||||||
@@ -52,6 +52,25 @@ pub fn do_write_line(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the modification time of the input file specified by file path
|
||||||
|
/// If an error occurs, it returns the current system time
|
||||||
|
pub fn get_modification_time(file_path: &str) -> String {
|
||||||
|
use chrono::{DateTime, Local};
|
||||||
|
use std::fs;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
let modification_time: SystemTime = fs::metadata(file_path)
|
||||||
|
.and_then(|m| m.modified())
|
||||||
|
.unwrap_or(SystemTime::now());
|
||||||
|
|
||||||
|
let modification_time: DateTime<Local> = modification_time.into();
|
||||||
|
let modification_time: String = modification_time
|
||||||
|
.format("%Y-%m-%d %H:%M:%S%.9f %z")
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
modification_time
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -115,4 +134,46 @@ mod tests {
|
|||||||
assert_line_written("foo bar\tbaz", true, 8, "foo bar baz");
|
assert_line_written("foo bar\tbaz", true, 8, "foo bar baz");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod modification_time {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn set_time() {
|
||||||
|
use chrono::{DateTime, Local};
|
||||||
|
use std::time::SystemTime;
|
||||||
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
|
let temp = NamedTempFile::new().unwrap();
|
||||||
|
// set file modification time equal to current time
|
||||||
|
let current = SystemTime::now();
|
||||||
|
let _ = temp.as_file().set_modified(current);
|
||||||
|
|
||||||
|
// format current time
|
||||||
|
let current: DateTime<Local> = current.into();
|
||||||
|
let current: String = current.format("%Y-%m-%d %H:%M:%S%.9f %z").to_string();
|
||||||
|
|
||||||
|
// verify
|
||||||
|
assert_eq!(
|
||||||
|
current,
|
||||||
|
get_modification_time(&temp.path().to_string_lossy())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_file() {
|
||||||
|
use chrono::{DateTime, Local};
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
let invalid_file = "target/utils/invalid-file";
|
||||||
|
|
||||||
|
// store current time before calling `get_modification_time`
|
||||||
|
// Because the file is invalid, it will return SystemTime::now()
|
||||||
|
// which will be greater than previously saved time
|
||||||
|
let current_time: DateTime<Local> = SystemTime::now().into();
|
||||||
|
let m_time: DateTime<Local> = get_modification_time(invalid_file).parse().unwrap();
|
||||||
|
|
||||||
|
assert!(m_time > current_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+125
-29
@@ -4,9 +4,11 @@
|
|||||||
// files that was distributed with this source code.
|
// files that was distributed with this source code.
|
||||||
|
|
||||||
use assert_cmd::cmd::Command;
|
use assert_cmd::cmd::Command;
|
||||||
|
use diffutilslib::assert_diff_eq;
|
||||||
use predicates::prelude::*;
|
use predicates::prelude::*;
|
||||||
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::{tempdir, NamedTempFile};
|
||||||
|
|
||||||
// Integration tests for the diffutils command
|
// Integration tests for the diffutils command
|
||||||
|
|
||||||
@@ -17,30 +19,53 @@ fn unknown_param() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
cmd.assert()
|
cmd.assert()
|
||||||
.code(predicate::eq(2))
|
.code(predicate::eq(2))
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::starts_with("Usage: "));
|
.stderr(predicate::str::starts_with("Unknown option: \"--foobar\""));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cannot_read_from_file() -> Result<(), Box<dyn std::error::Error>> {
|
fn cannot_read_files() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut cmd = Command::cargo_bin("diffutils")?;
|
|
||||||
cmd.arg("foo.txt").arg("bar.txt");
|
|
||||||
cmd.assert()
|
|
||||||
.code(predicate::eq(2))
|
|
||||||
.failure()
|
|
||||||
.stderr(predicate::str::starts_with("Failed to read from-file"));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn cannot_read_to_file() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let file = NamedTempFile::new()?;
|
let file = NamedTempFile::new()?;
|
||||||
|
|
||||||
|
let nofile = NamedTempFile::new()?;
|
||||||
|
let nopath = nofile.into_temp_path();
|
||||||
|
std::fs::remove_file(&nopath)?;
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let error_message = "No such file or directory";
|
||||||
|
#[cfg(windows)]
|
||||||
|
let error_message = "The system cannot find the file specified.";
|
||||||
|
|
||||||
let mut cmd = Command::cargo_bin("diffutils")?;
|
let mut cmd = Command::cargo_bin("diffutils")?;
|
||||||
cmd.arg(file.path()).arg("bar.txt");
|
cmd.arg(&nopath).arg(file.path());
|
||||||
cmd.assert()
|
cmd.assert()
|
||||||
.code(predicate::eq(2))
|
.code(predicate::eq(2))
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::starts_with("Failed to read to-file"));
|
.stderr(predicate::str::ends_with(format!(
|
||||||
|
": {}: {error_message}\n",
|
||||||
|
&nopath.as_os_str().to_string_lossy()
|
||||||
|
)));
|
||||||
|
|
||||||
|
let mut cmd = Command::cargo_bin("diffutils")?;
|
||||||
|
cmd.arg(file.path()).arg(&nopath);
|
||||||
|
cmd.assert()
|
||||||
|
.code(predicate::eq(2))
|
||||||
|
.failure()
|
||||||
|
.stderr(predicate::str::ends_with(format!(
|
||||||
|
": {}: {error_message}\n",
|
||||||
|
&nopath.as_os_str().to_string_lossy()
|
||||||
|
)));
|
||||||
|
|
||||||
|
let mut cmd = Command::cargo_bin("diffutils")?;
|
||||||
|
cmd.arg(&nopath).arg(&nopath);
|
||||||
|
cmd.assert().code(predicate::eq(2)).failure().stderr(
|
||||||
|
predicate::str::contains(format!(
|
||||||
|
": {}: {error_message}\n",
|
||||||
|
&nopath.as_os_str().to_string_lossy()
|
||||||
|
))
|
||||||
|
.count(2),
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,33 +198,104 @@ fn read_from_stdin() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
.arg(file1.path())
|
.arg(file1.path())
|
||||||
.arg("-")
|
.arg("-")
|
||||||
.write_stdin("bar\n");
|
.write_stdin("bar\n");
|
||||||
cmd.assert()
|
cmd.assert().code(predicate::eq(1)).failure();
|
||||||
.code(predicate::eq(1))
|
|
||||||
.failure()
|
let output = cmd.output().unwrap().stdout;
|
||||||
.stdout(predicate::eq(format!(
|
assert_diff_eq!(
|
||||||
"--- {}\t\n+++ /dev/stdin\t\n@@ -1 +1 @@\n-foo\n+bar\n",
|
output,
|
||||||
|
format!(
|
||||||
|
"--- {}\tTIMESTAMP\n+++ -\tTIMESTAMP\n@@ -1 +1 @@\n-foo\n+bar\n",
|
||||||
file1.path().to_string_lossy()
|
file1.path().to_string_lossy()
|
||||||
)));
|
)
|
||||||
|
);
|
||||||
|
|
||||||
let mut cmd = Command::cargo_bin("diffutils")?;
|
let mut cmd = Command::cargo_bin("diffutils")?;
|
||||||
cmd.arg("-u")
|
cmd.arg("-u")
|
||||||
.arg("-")
|
.arg("-")
|
||||||
.arg(file2.path())
|
.arg(file2.path())
|
||||||
.write_stdin("foo\n");
|
.write_stdin("foo\n");
|
||||||
cmd.assert()
|
cmd.assert().code(predicate::eq(1)).failure();
|
||||||
.code(predicate::eq(1))
|
|
||||||
.failure()
|
let output = cmd.output().unwrap().stdout;
|
||||||
.stdout(predicate::eq(format!(
|
assert_diff_eq!(
|
||||||
"--- /dev/stdin\t\n+++ {}\t\n@@ -1 +1 @@\n-foo\n+bar\n",
|
output,
|
||||||
|
format!(
|
||||||
|
"--- -\tTIMESTAMP\n+++ {}\tTIMESTAMP\n@@ -1 +1 @@\n-foo\n+bar\n",
|
||||||
file2.path().to_string_lossy()
|
file2.path().to_string_lossy()
|
||||||
)));
|
)
|
||||||
|
);
|
||||||
|
|
||||||
let mut cmd = Command::cargo_bin("diffutils")?;
|
let mut cmd = Command::cargo_bin("diffutils")?;
|
||||||
cmd.arg("-u").arg("-").arg("-").write_stdin("foo\n");
|
cmd.arg("-u").arg("-").arg("-");
|
||||||
cmd.assert()
|
cmd.assert()
|
||||||
.code(predicate::eq(0))
|
.code(predicate::eq(0))
|
||||||
.success()
|
.success()
|
||||||
.stdout(predicate::str::is_empty());
|
.stdout(predicate::str::is_empty());
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
let mut cmd = Command::cargo_bin("diffutils")?;
|
||||||
|
cmd.arg("-u")
|
||||||
|
.arg(file1.path())
|
||||||
|
.arg("/dev/stdin")
|
||||||
|
.write_stdin("bar\n");
|
||||||
|
cmd.assert().code(predicate::eq(1)).failure();
|
||||||
|
|
||||||
|
let output = cmd.output().unwrap().stdout;
|
||||||
|
assert_diff_eq!(
|
||||||
|
output,
|
||||||
|
format!(
|
||||||
|
"--- {}\tTIMESTAMP\n+++ /dev/stdin\tTIMESTAMP\n@@ -1 +1 @@\n-foo\n+bar\n",
|
||||||
|
file1.path().to_string_lossy()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compare_file_to_directory() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let tmp_dir = tempdir()?;
|
||||||
|
|
||||||
|
let directory = tmp_dir.path().join("d");
|
||||||
|
let _ = std::fs::create_dir(&directory);
|
||||||
|
|
||||||
|
let a_path = tmp_dir.path().join("a");
|
||||||
|
let mut a = File::create(&a_path).unwrap();
|
||||||
|
a.write_all(b"a\n").unwrap();
|
||||||
|
|
||||||
|
let da_path = directory.join("a");
|
||||||
|
let mut da = File::create(&da_path).unwrap();
|
||||||
|
da.write_all(b"da\n").unwrap();
|
||||||
|
|
||||||
|
let mut cmd = Command::cargo_bin("diffutils")?;
|
||||||
|
cmd.arg("-u").arg(&directory).arg(&a_path);
|
||||||
|
cmd.assert().code(predicate::eq(1)).failure();
|
||||||
|
|
||||||
|
let output = cmd.output().unwrap().stdout;
|
||||||
|
assert_diff_eq!(
|
||||||
|
output,
|
||||||
|
format!(
|
||||||
|
"--- {}\tTIMESTAMP\n+++ {}\tTIMESTAMP\n@@ -1 +1 @@\n-da\n+a\n",
|
||||||
|
da_path.display(),
|
||||||
|
a_path.display()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut cmd = Command::cargo_bin("diffutils")?;
|
||||||
|
cmd.arg("-u").arg(&a_path).arg(&directory);
|
||||||
|
cmd.assert().code(predicate::eq(1)).failure();
|
||||||
|
|
||||||
|
let output = cmd.output().unwrap().stdout;
|
||||||
|
assert_diff_eq!(
|
||||||
|
output,
|
||||||
|
format!(
|
||||||
|
"--- {}\tTIMESTAMP\n+++ {}\tTIMESTAMP\n@@ -1 +1 @@\n-a\n+da\n",
|
||||||
|
a_path.display(),
|
||||||
|
da_path.display()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
# By default it expects a release build of the diffutils binary, but a
|
# By default it expects a release build of the diffutils binary, but a
|
||||||
# different build profile can be specified as an argument
|
# different build profile can be specified as an argument
|
||||||
# (e.g. 'dev' or 'test').
|
# (e.g. 'dev' or 'test').
|
||||||
# Unless overriden by the $TESTS environment variable, all tests in the test
|
# Unless overridden by the $TESTS environment variable, all tests in the test
|
||||||
# suite will be run. Tests targeting a command that is not yet implemented
|
# suite will be run. Tests targeting a command that is not yet implemented
|
||||||
# (e.g. cmp, diff3 or sdiff) are skipped.
|
# (e.g. cmp, diff3 or sdiff) are skipped.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user