mirror of
https://github.com/uutils/diffutils.git
synced 2026-06-29 15:15:15 -04:00
Compare commits
214 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c38fe5f2e5 | |||
| 44565de705 | |||
| 8c8c1db5c6 | |||
| 3e02493701 | |||
| c6e8b46d21 | |||
| 125fc298c5 | |||
| 16673cf466 | |||
| f66ad85757 | |||
| de9bf94d01 | |||
| 940a0e00b6 | |||
| 7ddc6c6c4b | |||
| 8997ac06b8 | |||
| df90e37566 | |||
| f25cad8497 | |||
| a09dcac41d | |||
| 98bf765a98 | |||
| 1e1e968027 | |||
| 4eee9cefa0 | |||
| 67589b9331 | |||
| 83f6d2db7c | |||
| b193ea0c43 | |||
| 5f2ba7a84c | |||
| 15473edcd7 | |||
| 30b6bd2523 | |||
| 590a4b405e | |||
| 418596138e | |||
| eadc8c3dc5 | |||
| 2806ec2029 | |||
| dbd60416e6 | |||
| 392b8fa07b | |||
| 44645e5428 | |||
| 644a794067 | |||
| 19b79efd76 | |||
| 3380bab935 | |||
| a0a05eeba9 | |||
| 611e380266 | |||
| af0dc993b8 | |||
| a680c4f467 | |||
| 0b604f67aa | |||
| cc67cbcc59 | |||
| a95ca0062f | |||
| b59d9be943 | |||
| 3654b82a6d | |||
| 7df02399ba | |||
| 03fe614087 | |||
| 8261d790f4 | |||
| 168dae3aee | |||
| c7d4140fa3 | |||
| dee3bc1d66 | |||
| fce0881e27 | |||
| 45b3072534 | |||
| a3e57c950e | |||
| 1ef6923b7d | |||
| dff98a2969 | |||
| 8105420bb4 | |||
| 5b791e8bf6 | |||
| b31df0b5e8 | |||
| c02273c827 | |||
| 199c7f169c | |||
| 978390c14d | |||
| 87ccc8e4c2 | |||
| 9bc53486df | |||
| 0d7e4d82ae | |||
| 360bff50ed | |||
| 26ee98dfaa | |||
| 009d64acd2 | |||
| b53d4f427c | |||
| 8448fd8068 | |||
| d573c3ae1d | |||
| ca1c4c3618 | |||
| ba1cac3c20 | |||
| 949cccebd4 | |||
| bbdfa1b765 | |||
| 2f1a89173a | |||
| f9553984f4 | |||
| 59920040f6 | |||
| 44c195c0b2 | |||
| fdc69921e6 | |||
| 4ff2d6b182 | |||
| 39e092488b | |||
| dcd3dfd6e0 | |||
| 90bed40046 | |||
| 1575aec22c | |||
| 4f2f869021 | |||
| 3101aa1aff | |||
| 14b062251f | |||
| 0e11811ce1 | |||
| 3de1930bbe | |||
| 889e7bb7cc | |||
| 1910cbfe58 | |||
| c70cc1921c | |||
| 933230e103 | |||
| a316262603 | |||
| 0bf04b4395 | |||
| 7480068e7d | |||
| 763074a804 | |||
| fac8dab182 | |||
| 2e681301b4 | |||
| 50057412bd | |||
| 68292b370d | |||
| 26bcc102c0 | |||
| 50198ef2c1 | |||
| bfdbf6d7b2 | |||
| f75c187971 | |||
| d07c0438b5 | |||
| 72c7802f06 | |||
| c1b66e4a47 | |||
| 9103365691 | |||
| 7574243de1 | |||
| 6f3834d69d | |||
| d8b91fd60e | |||
| 7c9c2a1ab2 | |||
| 63d51bcd69 | |||
| d5bce65a29 | |||
| 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 |
+27
-10
@@ -28,6 +28,15 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: install GNU patch on MacOS
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew install gpatch
|
||||
echo "/opt/homebrew/opt/gpatch/libexec/gnubin" >> "$GITHUB_PATH"
|
||||
- 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
|
||||
|
||||
fmt:
|
||||
@@ -95,21 +104,30 @@ jobs:
|
||||
if [ -n "${{ matrix.job.toolchain }}" ]; then TOOLCHAIN="${{ matrix.job.toolchain }}" ; fi
|
||||
outputs TOOLCHAIN
|
||||
# target-specific options
|
||||
# * CARGO_FEATURES_OPTION
|
||||
CARGO_FEATURES_OPTION='--all -- --check' ; ## default to '--all-features' for code coverage
|
||||
# * CODECOV_FLAGS
|
||||
CODECOV_FLAGS=$( echo "${{ matrix.job.os }}" | sed 's/[^[:alnum:]]/_/g' )
|
||||
outputs CODECOV_FLAGS
|
||||
|
||||
- name: rust toolchain ~ install
|
||||
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
|
||||
echo "/opt/homebrew/opt/gpatch/libexec/gnubin" >> "$GITHUB_PATH"
|
||||
- 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
|
||||
run: cargo test ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast
|
||||
run: cargo test --all-features --no-fail-fast
|
||||
env:
|
||||
CARGO_INCREMENTAL: "0"
|
||||
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"
|
||||
LLVM_PROFILE_FILE: "diffutils-%p-%m.profraw"
|
||||
- name: "`grcov` ~ install"
|
||||
id: build_grcov
|
||||
shell: bash
|
||||
@@ -137,16 +155,15 @@ jobs:
|
||||
COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info"
|
||||
mkdir -p "${COVERAGE_REPORT_DIR}"
|
||||
# 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
|
||||
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
|
||||
- name: Upload coverage results (to Codecov.io)
|
||||
uses: codecov/codecov-action@v3
|
||||
# if: steps.vars.outputs.HAS_CODECOV_TOKEN
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
# token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: ${{ steps.coverage.outputs.report }}
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ${{ steps.coverage.outputs.report }}
|
||||
## flags: IntegrationTests, UnitTests, ${{ steps.vars.outputs.CODECOV_FLAGS }}
|
||||
flags: ${{ steps.vars.outputs.CODECOV_FLAGS }}
|
||||
name: codecov-umbrella
|
||||
|
||||
@@ -41,9 +41,12 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
test-target:
|
||||
- { name: fuzz_cmp, should_pass: true }
|
||||
- { name: fuzz_cmp_args, should_pass: true }
|
||||
- { name: fuzz_ed, should_pass: true }
|
||||
- { name: fuzz_normal, should_pass: true }
|
||||
- { name: fuzz_patch, should_pass: true }
|
||||
- { name: fuzz_side, should_pass: true }
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
@@ -54,7 +57,7 @@ jobs:
|
||||
shared-key: "cargo-fuzz-cache-key"
|
||||
cache-directories: "fuzz/target"
|
||||
- name: Restore Cached Corpus
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
key: corpus-cache-${{ matrix.test-target.name }}
|
||||
path: |
|
||||
@@ -65,7 +68,7 @@ jobs:
|
||||
run: |
|
||||
cargo +nightly fuzz run ${{ matrix.test-target.name }} -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
|
||||
- name: Save Corpus Cache
|
||||
uses: actions/cache/save@v4
|
||||
uses: actions/cache/save@v5
|
||||
with:
|
||||
key: corpus-cache-${{ matrix.test-target.name }}
|
||||
path: |
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
# This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist
|
||||
#
|
||||
# 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 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 dist-able).
|
||||
#
|
||||
# If PACKAGE_NAME isn't specified, then the announcement will be for all
|
||||
# (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:
|
||||
pull_request:
|
||||
push:
|
||||
tags:
|
||||
- '**[0-9]+.[0-9]+.[0-9]+*'
|
||||
|
||||
jobs:
|
||||
# Run 'dist plan' (or host) to determine what tasks we need to do
|
||||
plan:
|
||||
runs-on: "ubuntu-22.04"
|
||||
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:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Install 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.30.3/cargo-dist-installer.sh | sh"
|
||||
- name: Cache dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/dist
|
||||
# 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: |
|
||||
dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json
|
||||
echo "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 dist in create-release.
|
||||
# Each member of the matrix has the following arguments:
|
||||
#
|
||||
# - runner: the github runner
|
||||
# - dist-args: cli flags to pass to dist
|
||||
# - install-dist: expression to run to install 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 }}
|
||||
container: ${{ matrix.container && matrix.container.image || null }}
|
||||
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:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Install Rust non-interactively if not already installed
|
||||
if: ${{ matrix.container }}
|
||||
run: |
|
||||
if ! command -v cargo > /dev/null 2>&1; then
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
fi
|
||||
- name: Install dist
|
||||
run: ${{ matrix.install_dist.run }}
|
||||
# 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
|
||||
dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json
|
||||
echo "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"
|
||||
dist print-upload-files-from-manifest --manifest 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-22.04"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Install cached dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/dist
|
||||
# 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: |
|
||||
dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
|
||||
echo "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 plan, local and global didn't fail (skipped is fine)
|
||||
if: ${{ always() && needs.plan.result == 'success' && 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-22.04"
|
||||
outputs:
|
||||
val: ${{ steps.host.outputs.manifest }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Install cached dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/dist
|
||||
# Fetch artifacts from scratch-storage
|
||||
- name: Fetch artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: target/distrib/
|
||||
merge-multiple: true
|
||||
- id: host
|
||||
shell: bash
|
||||
run: |
|
||||
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
|
||||
- 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
|
||||
env:
|
||||
PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}"
|
||||
ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}"
|
||||
ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}"
|
||||
RELEASE_COMMIT: "${{ github.sha }}"
|
||||
run: |
|
||||
# Write and read notes from a file to avoid quoting breaking things
|
||||
echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt
|
||||
|
||||
gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*
|
||||
|
||||
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-22.04"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
Generated
+253
-60
@@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
@@ -11,6 +11,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.6"
|
||||
@@ -19,13 +28,13 @@ checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||
|
||||
[[package]]
|
||||
name = "assert_cmd"
|
||||
version = "2.0.14"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8"
|
||||
checksum = "bcbb6924530aa9e0432442af08bbcafdad182db80d2e560da42a6d442535bf85"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"bstr",
|
||||
"doc-comment",
|
||||
"libc",
|
||||
"predicates",
|
||||
"predicates-core",
|
||||
"predicates-tree",
|
||||
@@ -55,12 +64,43 @@ dependencies = [
|
||||
"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]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
@@ -75,10 +115,12 @@ checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||
|
||||
[[package]]
|
||||
name = "diffutils"
|
||||
version = "0.3.0"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"assert_cmd",
|
||||
"chrono",
|
||||
"diff",
|
||||
"itoa",
|
||||
"predicates",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
@@ -87,17 +129,11 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doc-comment"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.8"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
@@ -105,30 +141,86 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.0.1"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||
checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
name = "getrandom"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[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 = "itoa"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.178"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.13"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
@@ -152,10 +244,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "3.1.0"
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "3.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"difflib",
|
||||
@@ -183,9 +281,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66"
|
||||
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
|
||||
dependencies = [
|
||||
"diff",
|
||||
"yansi",
|
||||
@@ -211,9 +309,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.3"
|
||||
version = "1.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
||||
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -223,9 +321,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.5"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
||||
checksum = "722166aa0d7438abbaa4d5cc2c649dac844e8c56d82fb3d33e9c34b5cd268fc6"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -234,15 +332,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.31"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
|
||||
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -293,12 +391,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.10.0"
|
||||
version = "3.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67"
|
||||
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
@@ -317,9 +416,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.11"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
@@ -330,6 +429,69 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.13.3+wasi-0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -362,23 +524,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.2"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d98532992affa02e52709d5b4d145a3668ae10d9081eea4a7f26f719a8476f71"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
@@ -387,48 +565,63 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.1"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7269c1442e75af9fa59290383f7665b828efc76c429cc0b7f2ecb33cf51ebae"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.1"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f70ab2cebf332b7ecbdd98900c2da5298a8c862472fb35c75fc297eabb9d89b8"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.1"
|
||||
version = "0.52.6"
|
||||
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]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.1"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3480ac194b55ae274a7e135c21645656825da4a7f5b6e9286291b2113c94a78b"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.1"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42c46bab241c121402d1cb47d028ea3680ee2f359dcc287482dcf7fdddc73363"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.1"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc885a4332ee1afb9a1bacf11514801011725570d35675abc229ce7e3afe4d20"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.1"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e440c60457f84b0bee09208e62acc7ade264b38c4453f6312b8c9ab1613e73c"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
||||
+21
-6
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "diffutils"
|
||||
version = "0.3.0"
|
||||
version = "0.5.0"
|
||||
edition = "2021"
|
||||
description = "A CLI app for generating diff files"
|
||||
license = "MIT OR Apache-2.0"
|
||||
@@ -15,13 +15,28 @@ name = "diffutils"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
diff = "0.1.10"
|
||||
regex = "1.10.3"
|
||||
chrono = "0.4.38"
|
||||
diff = "0.1.13"
|
||||
itoa = "1.0.11"
|
||||
regex = "1.10.4"
|
||||
same-file = "1.0.6"
|
||||
unicode-width = "0.1.11"
|
||||
unicode-width = "0.2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1"
|
||||
pretty_assertions = "1.4.0"
|
||||
assert_cmd = "2.0.14"
|
||||
predicates = "3.1.0"
|
||||
tempfile = "3.10.0"
|
||||
tempfile = "3.10.1"
|
||||
|
||||
[profile.release]
|
||||
lto = "thin"
|
||||
codegen-units = 1
|
||||
|
||||
[profile.release-fast]
|
||||
inherits = "release"
|
||||
panic = "abort"
|
||||
|
||||
# The profile that 'dist' will build with
|
||||
[profile.dist]
|
||||
inherits = "release"
|
||||
lto = "thin"
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
Copyright (c) Michael Howell
|
||||
Copyright (c) uutils developers
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
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
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
[](https://codecov.io/gh/uutils/diffutils)
|
||||
|
||||
The goal of this package is to be a drop-in replacement for the [diffutils commands](https://www.gnu.org/software/diffutils/) in Rust.
|
||||
The goal of this package is to be a drop-in replacement for the [diffutils commands](https://www.gnu.org/software/diffutils/) (diff, cmp, diff3, sdiff) in Rust.
|
||||
|
||||
Based on the incomplete diff generator in https://github.com/rust-lang/rust/blob/master/src/tools/compiletest/src/runtest.rs, and made to be compatible with GNU's diff and patch tools.
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
[workspace]
|
||||
members = ["cargo:."]
|
||||
|
||||
# Config for 'dist'
|
||||
[dist]
|
||||
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
|
||||
cargo-dist-version = "0.30.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", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
|
||||
Generated
+447
@@ -0,0 +1,447 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||
|
||||
[[package]]
|
||||
name = "diffutils"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"diff",
|
||||
"itoa",
|
||||
"regex",
|
||||
"same-file",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"log",
|
||||
"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 = "itoa"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.178"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.112"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21f182278bf2d2bcb3c88b1b08a37df029d71ce3d3ae26168e3c653b213b99d4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "unified-diff-fuzz"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"diffutils",
|
||||
"libfuzzer-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.1+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
+18
-2
@@ -9,13 +9,25 @@ edition = "2018"
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
libfuzzer-sys = "0.4"
|
||||
libfuzzer-sys = "0.4.7"
|
||||
diffutils = { path = "../" }
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
[workspace]
|
||||
members = ["."]
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_cmp"
|
||||
path = "fuzz_targets/fuzz_cmp.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_cmp_args"
|
||||
path = "fuzz_targets/fuzz_cmp_args.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_patch"
|
||||
path = "fuzz_targets/fuzz_patch.rs"
|
||||
@@ -35,4 +47,8 @@ path = "fuzz_targets/fuzz_ed.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_side"
|
||||
path = "fuzz_targets/fuzz_side.rs"
|
||||
test = false
|
||||
doc = false
|
||||
@@ -0,0 +1,36 @@
|
||||
"-l"
|
||||
"--verbose"
|
||||
"-b"
|
||||
"--print-bytes"
|
||||
"-lb"
|
||||
"-bl"
|
||||
"-n"
|
||||
"--bytes"
|
||||
"--bytes="
|
||||
"--bytes=1024"
|
||||
"--bytes=99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
||||
"-i"
|
||||
"--ignore-initial"
|
||||
"--ignore-initial="
|
||||
"--ignore-initial=1024"
|
||||
"--ignore-initial=99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999:9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
||||
"-s"
|
||||
"-q"
|
||||
"--quiet"
|
||||
"--silent"
|
||||
"-"
|
||||
"--"
|
||||
"1kB"
|
||||
"1G"
|
||||
"1GB"
|
||||
"1T"
|
||||
"1TB"
|
||||
"1P"
|
||||
"1PB"
|
||||
"1Z"
|
||||
"1ZB"
|
||||
"1Y"
|
||||
"1YB"
|
||||
"1Y"
|
||||
"0"
|
||||
"1:2"
|
||||
@@ -0,0 +1,51 @@
|
||||
#![no_main]
|
||||
#[macro_use]
|
||||
extern crate libfuzzer_sys;
|
||||
use diffutilslib::cmp::{self, Cmp};
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
fn os(s: &str) -> OsString {
|
||||
OsString::from(s)
|
||||
}
|
||||
|
||||
fuzz_target!(|x: (Vec<u8>, Vec<u8>)| {
|
||||
let args = vec!["cmp", "-l", "-b", "target/fuzz.cmp.a", "target/fuzz.cmp.b"]
|
||||
.into_iter()
|
||||
.map(|s| os(s))
|
||||
.peekable();
|
||||
|
||||
let (from, to) = x;
|
||||
|
||||
File::create("target/fuzz.cmp.a")
|
||||
.unwrap()
|
||||
.write_all(&from)
|
||||
.unwrap();
|
||||
|
||||
File::create("target/fuzz.cmp.b")
|
||||
.unwrap()
|
||||
.write_all(&to)
|
||||
.unwrap();
|
||||
|
||||
let params =
|
||||
cmp::parse_params(args).unwrap_or_else(|e| panic!("Failed to parse params: {}", e));
|
||||
let ret = cmp::cmp(¶ms);
|
||||
if from == to && !matches!(ret, Ok(Cmp::Equal)) {
|
||||
panic!(
|
||||
"target/fuzz.cmp.a and target/fuzz.cmp.b are equal, but cmp returned {:?}.",
|
||||
ret
|
||||
);
|
||||
} else if from != to && !matches!(ret, Ok(Cmp::Different)) {
|
||||
panic!(
|
||||
"target/fuzz.cmp.a and target/fuzz.cmp.b are different, but cmp returned {:?}.",
|
||||
ret
|
||||
);
|
||||
} else if ret.is_err() {
|
||||
panic!(
|
||||
"target/fuzz.cmp.a and target/fuzz.cmp.b caused cmp to error ({:?}).",
|
||||
ret
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
#![no_main]
|
||||
#[macro_use]
|
||||
extern crate libfuzzer_sys;
|
||||
use diffutilslib::cmp;
|
||||
|
||||
use libfuzzer_sys::Corpus;
|
||||
use std::ffi::OsString;
|
||||
|
||||
fn os(s: &str) -> OsString {
|
||||
OsString::from(s)
|
||||
}
|
||||
|
||||
fuzz_target!(|x: Vec<OsString>| -> Corpus {
|
||||
if x.len() > 6 {
|
||||
// Make sure we try to parse an option when we get longer args. x[0] will be
|
||||
// the executable name.
|
||||
if ![os("-l"), os("-b"), os("-s"), os("-n"), os("-i")].contains(&x[1]) {
|
||||
return Corpus::Reject;
|
||||
}
|
||||
}
|
||||
let _ = cmp::parse_params(x.into_iter().peekable());
|
||||
Corpus::Keep
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
#![no_main]
|
||||
#[macro_use]
|
||||
extern crate libfuzzer_sys;
|
||||
|
||||
use diffutilslib::side_diff;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use diffutilslib::params::Params;
|
||||
|
||||
fuzz_target!(|x: (Vec<u8>, Vec<u8>, /* usize, usize */ bool)| {
|
||||
let (original, new, /* width, tabsize, */ expand) = x;
|
||||
|
||||
// if width == 0 || tabsize == 0 {
|
||||
// return;
|
||||
// }
|
||||
|
||||
let params = Params {
|
||||
// width,
|
||||
// tabsize,
|
||||
expand_tabs: expand,
|
||||
..Default::default()
|
||||
};
|
||||
let mut output_buf = vec![];
|
||||
side_diff::diff(&original, &new, &mut output_buf, ¶ms);
|
||||
File::create("target/fuzz.file.original")
|
||||
.unwrap()
|
||||
.write_all(&original)
|
||||
.unwrap();
|
||||
File::create("target/fuzz.file.new")
|
||||
.unwrap()
|
||||
.write_all(&new)
|
||||
.unwrap();
|
||||
File::create("target/fuzz.file")
|
||||
.unwrap()
|
||||
.write_all(&original)
|
||||
.unwrap();
|
||||
File::create("target/fuzz.diff")
|
||||
.unwrap()
|
||||
.write_all(&output_buf)
|
||||
.unwrap();
|
||||
});
|
||||
+1185
File diff suppressed because it is too large
Load Diff
+36
-27
@@ -8,6 +8,7 @@ use std::io::Write;
|
||||
|
||||
use crate::params::Params;
|
||||
use crate::utils::do_write_line;
|
||||
use crate::utils::get_modification_time;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DiffLine {
|
||||
@@ -267,10 +268,14 @@ fn make_diff(
|
||||
|
||||
#[must_use]
|
||||
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!(
|
||||
"*** {0}\t\n--- {1}\t\n",
|
||||
"*** {0}\t{1}\n--- {2}\t{3}\n",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy()
|
||||
from_modified_time,
|
||||
params.to.to_string_lossy(),
|
||||
to_modified_time
|
||||
)
|
||||
.into_bytes();
|
||||
let diff_results = make_diff(expected, actual, params.context_count, params.brief);
|
||||
@@ -434,26 +439,26 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
File::create(&format!("{target}/ab.diff"))
|
||||
File::create(format!("{target}/ab.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alef")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg("--context")
|
||||
.stdin(File::open(&format!("{target}/ab.diff")).unwrap())
|
||||
.stdin(File::open(format!("{target}/ab.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//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);
|
||||
}
|
||||
}
|
||||
@@ -515,26 +520,26 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
File::create(&format!("{target}/ab_.diff"))
|
||||
File::create(format!("{target}/ab_.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alef_")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg("--context")
|
||||
.stdin(File::open(&format!("{target}/ab_.diff")).unwrap())
|
||||
.stdin(File::open(format!("{target}/ab_.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//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);
|
||||
}
|
||||
}
|
||||
@@ -599,26 +604,26 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
File::create(&format!("{target}/abx.diff"))
|
||||
File::create(format!("{target}/abx.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alefx")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alefx")).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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg("--context")
|
||||
.stdin(File::open(&format!("{target}/abx.diff")).unwrap())
|
||||
.stdin(File::open(format!("{target}/abx.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//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);
|
||||
}
|
||||
}
|
||||
@@ -686,26 +691,26 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
File::create(&format!("{target}/abr.diff"))
|
||||
File::create(format!("{target}/abr.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alefr")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alefr")).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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg("--context")
|
||||
.stdin(File::open(&format!("{target}/abr.diff")).unwrap())
|
||||
.stdin(File::open(format!("{target}/abr.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//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);
|
||||
}
|
||||
}
|
||||
@@ -717,6 +722,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_stop_early() {
|
||||
use crate::assert_diff_eq;
|
||||
|
||||
let from_filename = "foo";
|
||||
let from = ["a", "b", "c", ""].join("\n");
|
||||
let to_filename = "bar";
|
||||
@@ -731,9 +738,10 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let expected_full = [
|
||||
"*** foo\t",
|
||||
"--- bar\t",
|
||||
"*** foo\tTIMESTAMP",
|
||||
"--- bar\tTIMESTAMP",
|
||||
"***************",
|
||||
"*** 1,3 ****",
|
||||
" a",
|
||||
@@ -746,7 +754,7 @@ mod tests {
|
||||
"",
|
||||
]
|
||||
.join("\n");
|
||||
assert_eq!(diff_full, expected_full.as_bytes());
|
||||
assert_diff_eq!(diff_full, expected_full);
|
||||
|
||||
let diff_brief = diff(
|
||||
from.as_bytes(),
|
||||
@@ -758,8 +766,9 @@ mod tests {
|
||||
..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(
|
||||
from.as_bytes(),
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
// This file is part of the uutils diffutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE-*
|
||||
// files that was distributed with this source code.
|
||||
|
||||
use crate::params::{parse_params, Format};
|
||||
use crate::utils::report_failure_to_read_input_file;
|
||||
use crate::{context_diff, ed_diff, normal_diff, side_diff, unified_diff};
|
||||
use std::env::ArgsOs;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io::{self, stdout, Read, Write};
|
||||
use std::iter::Peekable;
|
||||
use std::process::{exit, ExitCode};
|
||||
|
||||
// Exit codes are documented at
|
||||
// https://www.gnu.org/software/diffutils/manual/html_node/Invoking-diff.html.
|
||||
// An exit status of 0 means no differences were found,
|
||||
// 1 means some differences were found,
|
||||
// and 2 means trouble.
|
||||
pub fn main(opts: Peekable<ArgsOs>) -> ExitCode {
|
||||
let params = parse_params(opts).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
});
|
||||
// if from and to are the same file, no need to perform any comparison
|
||||
let maybe_report_identical_files = || {
|
||||
if params.report_identical_files {
|
||||
println!(
|
||||
"Files {} and {} are identical",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy(),
|
||||
);
|
||||
}
|
||||
};
|
||||
if params.from == "-" && params.to == "-"
|
||||
|| same_file::is_same_file(¶ms.from, ¶ms.to).unwrap_or(false)
|
||||
{
|
||||
maybe_report_identical_files();
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
|
||||
// read files
|
||||
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,
|
||||
Err(e) => {
|
||||
report_failure_to_read_input_file(¶ms.executable, ¶ms.from, &e);
|
||||
io_error = true;
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
let to_content = match read_file_contents(¶ms.to) {
|
||||
Ok(to_content) => to_content,
|
||||
Err(e) => {
|
||||
report_failure_to_read_input_file(¶ms.executable, ¶ms.to, &e);
|
||||
io_error = true;
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
if io_error {
|
||||
return ExitCode::from(2);
|
||||
}
|
||||
|
||||
// run diff
|
||||
let result: Vec<u8> = match params.format {
|
||||
Format::Normal => normal_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Unified => unified_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Context => context_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Ed => ed_diff::diff(&from_content, &to_content, ¶ms).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
}),
|
||||
Format::SideBySide => {
|
||||
let mut output = stdout().lock();
|
||||
side_diff::diff(&from_content, &to_content, &mut output, ¶ms)
|
||||
}
|
||||
};
|
||||
if params.brief && !result.is_empty() {
|
||||
println!(
|
||||
"Files {} and {} differ",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy()
|
||||
);
|
||||
} else {
|
||||
io::stdout().write_all(&result).unwrap();
|
||||
}
|
||||
if result.is_empty() {
|
||||
maybe_report_identical_files();
|
||||
ExitCode::SUCCESS
|
||||
} else {
|
||||
ExitCode::from(1)
|
||||
}
|
||||
}
|
||||
+55
-46
@@ -188,9 +188,8 @@ mod tests {
|
||||
for &d in &[0, 1, 2] {
|
||||
for &e 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::process::Command;
|
||||
let mut alef = Vec::new();
|
||||
let mut bet = Vec::new();
|
||||
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.
|
||||
// We want it to turn the alef into bet.
|
||||
let diff = diff_w(&alef, &bet, &format!("{target}/alef")).unwrap();
|
||||
File::create("target/ab.ed")
|
||||
File::create(format!("{target}/ab.ed"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alef")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("ed")
|
||||
.arg(&format!("{target}/alef"))
|
||||
.stdin(File::open("target/ab.ed").unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
let alef = fs::read(&format!("{target}/alef")).unwrap();
|
||||
assert_eq!(alef, bet);
|
||||
#[cfg(not(windows))] // there's no ed on windows
|
||||
{
|
||||
use std::process::Command;
|
||||
let output = Command::new("ed")
|
||||
.arg(format!("{target}/alef"))
|
||||
.stdin(File::open(format!("{target}/ab.ed")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//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 &e 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::process::Command;
|
||||
let mut alef = Vec::new();
|
||||
let mut bet = Vec::new();
|
||||
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.
|
||||
// We want it to turn the alef into bet.
|
||||
let diff = diff_w(&alef, &bet, "target/alef_").unwrap();
|
||||
File::create("target/ab_.ed")
|
||||
let diff = diff_w(&alef, &bet, &format!("{target}/alef_")).unwrap();
|
||||
File::create(format!("{target}/ab_.ed"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create("target/alef_").unwrap();
|
||||
let mut fa = File::create(format!("{target}/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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("ed")
|
||||
.arg("target/alef_")
|
||||
.stdin(File::open("target/ab_.ed").unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
let alef = fs::read("target/alef_").unwrap();
|
||||
assert_eq!(alef, bet);
|
||||
#[cfg(not(windows))] // there's no ed on windows
|
||||
{
|
||||
use std::process::Command;
|
||||
let output = Command::new("ed")
|
||||
.arg(format!("{target}/alef_"))
|
||||
.stdin(File::open(format!("{target}/ab_.ed")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//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 &e 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::process::Command;
|
||||
let mut alef = Vec::new();
|
||||
let mut bet = Vec::new();
|
||||
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.
|
||||
// We want it to turn the alef into bet.
|
||||
let diff = diff_w(&alef, &bet, &format!("{target}/alefr")).unwrap();
|
||||
File::create("target/abr.ed")
|
||||
File::create(format!("{target}/abr.ed"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alefr")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alefr")).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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("ed")
|
||||
.arg(&format!("{target}/alefr"))
|
||||
.stdin(File::open("target/abr.ed").unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
let alef = fs::read(&format!("{target}/alefr")).unwrap();
|
||||
assert_eq!(alef, bet);
|
||||
#[cfg(not(windows))] // there's no ed on windows
|
||||
{
|
||||
use std::process::Command;
|
||||
let output = Command::new("ed")
|
||||
.arg(format!("{target}/alefr"))
|
||||
.stdin(File::open(format!("{target}/abr.ed")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//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,7 +1,10 @@
|
||||
pub mod cmp;
|
||||
pub mod context_diff;
|
||||
pub mod ed_diff;
|
||||
pub mod macros;
|
||||
pub mod normal_diff;
|
||||
pub mod params;
|
||||
pub mod side_diff;
|
||||
pub mod unified_diff;
|
||||
pub mod utils;
|
||||
|
||||
@@ -9,4 +12,5 @@ pub mod utils;
|
||||
pub use context_diff::diff as context_diff;
|
||||
pub use ed_diff::diff as ed_diff;
|
||||
pub use normal_diff::diff as normal_diff;
|
||||
pub use side_diff::diff as side_by_side_diff;
|
||||
pub use unified_diff::diff as 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);
|
||||
}};
|
||||
}
|
||||
+66
-70
@@ -3,83 +3,79 @@
|
||||
// For the full copyright and license information, please view the LICENSE-*
|
||||
// files that was distributed with this source code.
|
||||
|
||||
use crate::params::{parse_params, Format};
|
||||
use std::env;
|
||||
|
||||
use std::fs;
|
||||
use std::io::{self, Write};
|
||||
use std::process::{exit, ExitCode};
|
||||
use std::{
|
||||
env::ArgsOs,
|
||||
ffi::{OsStr, OsString},
|
||||
iter::Peekable,
|
||||
path::{Path, PathBuf},
|
||||
process::ExitCode,
|
||||
};
|
||||
|
||||
mod cmp;
|
||||
mod context_diff;
|
||||
mod diff;
|
||||
mod ed_diff;
|
||||
mod macros;
|
||||
mod normal_diff;
|
||||
mod params;
|
||||
mod side_diff;
|
||||
mod unified_diff;
|
||||
mod utils;
|
||||
|
||||
// Exit codes are documented at
|
||||
// https://www.gnu.org/software/diffutils/manual/html_node/Invoking-diff.html.
|
||||
// An exit status of 0 means no differences were found,
|
||||
// 1 means some differences were found,
|
||||
// and 2 means trouble.
|
||||
fn main() -> ExitCode {
|
||||
let opts = env::args_os();
|
||||
let params = parse_params(opts).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
});
|
||||
// if from and to are the same file, no need to perform any comparison
|
||||
let maybe_report_identical_files = || {
|
||||
if params.report_identical_files {
|
||||
println!(
|
||||
"Files {} and {} are identical",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy(),
|
||||
);
|
||||
}
|
||||
};
|
||||
if same_file::is_same_file(¶ms.from, ¶ms.to).unwrap_or(false) {
|
||||
maybe_report_identical_files();
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
// read files
|
||||
let from_content = match fs::read(¶ms.from) {
|
||||
Ok(from_content) => from_content,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to read from-file: {e}");
|
||||
return ExitCode::from(2);
|
||||
}
|
||||
};
|
||||
let to_content = match fs::read(¶ms.to) {
|
||||
Ok(to_content) => to_content,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to read to-file: {e}");
|
||||
return ExitCode::from(2);
|
||||
}
|
||||
};
|
||||
// run diff
|
||||
let result: Vec<u8> = match params.format {
|
||||
Format::Normal => normal_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Unified => unified_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Context => context_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Ed => ed_diff::diff(&from_content, &to_content, ¶ms).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
}),
|
||||
};
|
||||
if params.brief && !result.is_empty() {
|
||||
println!(
|
||||
"Files {} and {} differ",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy()
|
||||
);
|
||||
} else {
|
||||
io::stdout().write_all(&result).unwrap();
|
||||
}
|
||||
if result.is_empty() {
|
||||
maybe_report_identical_files();
|
||||
ExitCode::SUCCESS
|
||||
} else {
|
||||
ExitCode::from(1)
|
||||
/// # Panics
|
||||
/// Panics if the binary path cannot be determined
|
||||
fn binary_path(args: &mut Peekable<ArgsOs>) -> PathBuf {
|
||||
match args.peek() {
|
||||
Some(ref s) if !s.is_empty() => PathBuf::from(s),
|
||||
_ => std::env::current_exe().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// #Panics
|
||||
/// Panics if path has no UTF-8 valid name
|
||||
fn name(binary_path: &Path) -> &OsStr {
|
||||
binary_path.file_stem().unwrap()
|
||||
}
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
fn usage(name: &str) {
|
||||
println!("{name} {VERSION} (multi-call binary)\n");
|
||||
println!("Usage: {name} [function [arguments...]]\n");
|
||||
println!("Currently defined functions:\n");
|
||||
println!(" cmp, diff\n");
|
||||
}
|
||||
|
||||
fn second_arg_error(name: &OsStr) -> ! {
|
||||
eprintln!("Expected utility name as second argument, got nothing.");
|
||||
usage(&name.to_string_lossy());
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let mut args = std::env::args_os().peekable();
|
||||
|
||||
let exe_path = binary_path(&mut args);
|
||||
let exe_name = name(&exe_path);
|
||||
|
||||
let util_name = if exe_name == "diffutils" {
|
||||
// Discard the item we peeked.
|
||||
let _ = args.next();
|
||||
|
||||
args.peek()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| second_arg_error(exe_name))
|
||||
} else {
|
||||
OsString::from(exe_name)
|
||||
};
|
||||
|
||||
match util_name.to_str() {
|
||||
Some("diff") => diff::main(args),
|
||||
Some("cmp") => cmp::main(args),
|
||||
Some(name) => {
|
||||
eprintln!("{name}: utility not supported");
|
||||
ExitCode::from(2)
|
||||
}
|
||||
None => second_arg_error(exe_name),
|
||||
}
|
||||
}
|
||||
|
||||
+24
-24
@@ -275,26 +275,26 @@ mod tests {
|
||||
// This test diff is intentionally reversed.
|
||||
// We want it to turn the alef into bet.
|
||||
let diff = diff(&alef, &bet, &Params::default());
|
||||
File::create(&format!("{target}/ab.diff"))
|
||||
File::create(format!("{target}/ab.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alef")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg(&format!("{target}/alef"))
|
||||
.stdin(File::open(&format!("{target}/ab.diff")).unwrap())
|
||||
.arg(format!("{target}/alef"))
|
||||
.stdin(File::open(format!("{target}/ab.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//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);
|
||||
}
|
||||
}
|
||||
@@ -367,27 +367,27 @@ mod tests {
|
||||
// This test diff is intentionally reversed.
|
||||
// We want it to turn the alef into bet.
|
||||
let diff = diff(&alef, &bet, &Params::default());
|
||||
File::create(&format!("{target}/abn.diff"))
|
||||
File::create(format!("{target}/abn.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alefn")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alefn")).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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg("--normal")
|
||||
.arg(&format!("{target}/alefn"))
|
||||
.stdin(File::open(&format!("{target}/abn.diff")).unwrap())
|
||||
.arg(format!("{target}/alefn"))
|
||||
.stdin(File::open(format!("{target}/abn.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//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);
|
||||
}
|
||||
}
|
||||
@@ -441,26 +441,26 @@ mod tests {
|
||||
// This test diff is intentionally reversed.
|
||||
// We want it to turn the alef into bet.
|
||||
let diff = diff(&alef, &bet, &Params::default());
|
||||
File::create(&format!("{target}/ab_.diff"))
|
||||
File::create(format!("{target}/ab_.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alef_")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg(&format!("{target}/alef_"))
|
||||
.stdin(File::open(&format!("{target}/ab_.diff")).unwrap())
|
||||
.arg(format!("{target}/alef_"))
|
||||
.stdin(File::open(format!("{target}/ab_.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//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);
|
||||
}
|
||||
}
|
||||
@@ -519,26 +519,26 @@ mod tests {
|
||||
// This test diff is intentionally reversed.
|
||||
// We want it to turn the alef into bet.
|
||||
let diff = diff(&alef, &bet, &Params::default());
|
||||
File::create(&format!("{target}/abr.diff"))
|
||||
File::create(format!("{target}/abr.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alefr")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alefr")).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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg(&format!("{target}/alefr"))
|
||||
.stdin(File::open(&format!("{target}/abr.diff")).unwrap())
|
||||
.arg(format!("{target}/alefr"))
|
||||
.stdin(File::open(format!("{target}/abr.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
||||
+528
-114
@@ -1,4 +1,6 @@
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::ffi::OsString;
|
||||
use std::iter::Peekable;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
@@ -9,21 +11,12 @@ pub enum Format {
|
||||
Unified,
|
||||
Context,
|
||||
Ed,
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn osstr_bytes(osstr: &OsStr) -> &[u8] {
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
osstr.as_bytes()
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
fn osstr_bytes(osstr: &OsStr) -> Vec<u8> {
|
||||
osstr.to_string_lossy().bytes().collect()
|
||||
SideBySide,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Params {
|
||||
pub executable: OsString,
|
||||
pub from: OsString,
|
||||
pub to: OsString,
|
||||
pub format: Format,
|
||||
@@ -32,11 +25,13 @@ pub struct Params {
|
||||
pub brief: bool,
|
||||
pub expand_tabs: bool,
|
||||
pub tabsize: usize,
|
||||
pub width: usize,
|
||||
}
|
||||
|
||||
impl Default for Params {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
executable: OsString::default(),
|
||||
from: OsString::default(),
|
||||
to: OsString::default(),
|
||||
format: Format::default(),
|
||||
@@ -45,33 +40,42 @@ impl Default for Params {
|
||||
brief: false,
|
||||
expand_tabs: false,
|
||||
tabsize: 8,
|
||||
width: 130,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params, String> {
|
||||
let mut opts = opts.into_iter();
|
||||
pub fn parse_params<I: Iterator<Item = OsString>>(mut opts: Peekable<I>) -> Result<Params, String> {
|
||||
// parse CLI
|
||||
|
||||
let Some(exe) = opts.next() else {
|
||||
let Some(executable) = opts.next() else {
|
||||
return Err("Usage: <exe> <from> <to>".to_string());
|
||||
};
|
||||
let mut params = Params::default();
|
||||
let mut params = Params {
|
||||
executable,
|
||||
..Default::default()
|
||||
};
|
||||
let mut from = None;
|
||||
let mut to = None;
|
||||
let mut format = None;
|
||||
let mut context = None;
|
||||
let tabsize_re = Regex::new(r"^--tabsize=(?<num>\d+)$").unwrap();
|
||||
let width_re = Regex::new(r"--width=(?P<long>\d+)$").unwrap();
|
||||
while let Some(param) = opts.next() {
|
||||
let next_param = opts.peek();
|
||||
if param == "--" {
|
||||
break;
|
||||
}
|
||||
if param == "-" {
|
||||
if from.is_none() {
|
||||
from = Some(OsString::from("/dev/stdin"));
|
||||
from = Some(param);
|
||||
} else if to.is_none() {
|
||||
to = Some(OsString::from("/dev/stdin"));
|
||||
to = Some(param);
|
||||
} else {
|
||||
return Err(format!("Usage: {} <from> <to>", exe.to_string_lossy()));
|
||||
return Err(format!(
|
||||
"Usage: {} <from> <to>",
|
||||
params.executable.to_string_lossy()
|
||||
));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -87,6 +91,48 @@ pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params,
|
||||
params.expand_tabs = true;
|
||||
continue;
|
||||
}
|
||||
if param == "--normal" {
|
||||
if format.is_some() && format != Some(Format::Normal) {
|
||||
return Err("Conflicting output style options".to_string());
|
||||
}
|
||||
format = Some(Format::Normal);
|
||||
continue;
|
||||
}
|
||||
if param == "-e" || param == "--ed" {
|
||||
if format.is_some() && format != Some(Format::Ed) {
|
||||
return Err("Conflicting output style options".to_string());
|
||||
}
|
||||
format = Some(Format::Ed);
|
||||
continue;
|
||||
}
|
||||
if param == "-y" || param == "--side-by-side" {
|
||||
if format.is_some() && format != Some(Format::SideBySide) {
|
||||
return Err("Conflicting output style option".to_string());
|
||||
}
|
||||
format = Some(Format::SideBySide);
|
||||
continue;
|
||||
}
|
||||
if width_re.is_match(param.to_string_lossy().as_ref()) {
|
||||
let param = param.into_string().unwrap();
|
||||
let width_str: &str = width_re
|
||||
.captures(param.as_str())
|
||||
.unwrap()
|
||||
.name("long")
|
||||
.unwrap()
|
||||
.as_str();
|
||||
|
||||
params.width = match width_str.parse::<usize>() {
|
||||
Ok(num) => {
|
||||
if num == 0 {
|
||||
return Err("invalid width «0»".to_string());
|
||||
}
|
||||
|
||||
num
|
||||
}
|
||||
Err(_) => return Err(format!("invalid width «{width_str}»")),
|
||||
};
|
||||
continue;
|
||||
}
|
||||
if tabsize_re.is_match(param.to_string_lossy().as_ref()) {
|
||||
// Because param matches the regular expression,
|
||||
// it is safe to assume it is valid UTF-8.
|
||||
@@ -98,70 +144,68 @@ pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params,
|
||||
.unwrap()
|
||||
.as_str();
|
||||
params.tabsize = match tabsize_str.parse::<usize>() {
|
||||
Ok(num) => num,
|
||||
Ok(num) => {
|
||||
if num == 0 {
|
||||
return Err("invalid tabsize «0»".to_string());
|
||||
}
|
||||
|
||||
num
|
||||
}
|
||||
Err(_) => return Err(format!("invalid tabsize «{tabsize_str}»")),
|
||||
};
|
||||
|
||||
continue;
|
||||
}
|
||||
let p = osstr_bytes(¶m);
|
||||
if p.first() == Some(&b'-') && p.get(1) != Some(&b'-') {
|
||||
let mut bit = p[1..].iter().copied().peekable();
|
||||
// Can't use a for loop because `diff -30u` is supposed to make a diff
|
||||
// with 30 lines of context.
|
||||
while let Some(b) = bit.next() {
|
||||
match b {
|
||||
b'0'..=b'9' => {
|
||||
params.context_count = (b - b'0') as usize;
|
||||
while let Some(b'0'..=b'9') = bit.peek() {
|
||||
params.context_count *= 10;
|
||||
params.context_count += (bit.next().unwrap() - b'0') as usize;
|
||||
}
|
||||
match match_context_diff_params(¶m, next_param, format) {
|
||||
Ok(DiffStyleMatch {
|
||||
is_match,
|
||||
context_count,
|
||||
next_param_consumed,
|
||||
}) => {
|
||||
if is_match {
|
||||
format = Some(Format::Context);
|
||||
if context_count.is_some() {
|
||||
context = context_count;
|
||||
}
|
||||
b'c' => {
|
||||
if format.is_some() && format != Some(Format::Context) {
|
||||
return Err("Conflicting output style options".to_string());
|
||||
}
|
||||
format = Some(Format::Context);
|
||||
if next_param_consumed {
|
||||
opts.next();
|
||||
}
|
||||
b'e' => {
|
||||
if format.is_some() && format != Some(Format::Ed) {
|
||||
return Err("Conflicting output style options".to_string());
|
||||
}
|
||||
format = Some(Format::Ed);
|
||||
}
|
||||
b'u' => {
|
||||
if format.is_some() && format != Some(Format::Unified) {
|
||||
return Err("Conflicting output style options".to_string());
|
||||
}
|
||||
format = Some(Format::Unified);
|
||||
}
|
||||
b'U' => {
|
||||
if format.is_some() && format != Some(Format::Unified) {
|
||||
return Err("Conflicting output style options".to_string());
|
||||
}
|
||||
format = Some(Format::Unified);
|
||||
let context_count_maybe = if bit.peek().is_some() {
|
||||
String::from_utf8(bit.collect::<Vec<u8>>()).ok()
|
||||
} else {
|
||||
opts.next().map(|x| x.to_string_lossy().into_owned())
|
||||
};
|
||||
if let Some(context_count_maybe) =
|
||||
context_count_maybe.and_then(|x| x.parse().ok())
|
||||
{
|
||||
params.context_count = context_count_maybe;
|
||||
break;
|
||||
}
|
||||
return Err("Invalid context count".to_string());
|
||||
}
|
||||
_ => return Err(format!("Unknown option: {}", String::from_utf8_lossy(&[b]))),
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if from.is_none() {
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
match match_unified_diff_params(¶m, next_param, format) {
|
||||
Ok(DiffStyleMatch {
|
||||
is_match,
|
||||
context_count,
|
||||
next_param_consumed,
|
||||
}) => {
|
||||
if is_match {
|
||||
format = Some(Format::Unified);
|
||||
if context_count.is_some() {
|
||||
context = context_count;
|
||||
}
|
||||
if next_param_consumed {
|
||||
opts.next();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
if param.to_string_lossy().starts_with('-') {
|
||||
return Err(format!("Unknown option: {param:?}"));
|
||||
}
|
||||
if from.is_none() {
|
||||
from = Some(param);
|
||||
} else if to.is_none() {
|
||||
to = Some(param);
|
||||
} else {
|
||||
return Err(format!("Usage: {} <from> <to>", exe.to_string_lossy()));
|
||||
return Err(format!(
|
||||
"Usage: {} <from> <to>",
|
||||
params.executable.to_string_lossy()
|
||||
));
|
||||
}
|
||||
}
|
||||
params.from = if let Some(from) = from {
|
||||
@@ -169,19 +213,136 @@ pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params,
|
||||
} else if let Some(param) = opts.next() {
|
||||
param
|
||||
} else {
|
||||
return Err(format!("Usage: {} <from> <to>", exe.to_string_lossy()));
|
||||
return Err(format!(
|
||||
"Usage: {} <from> <to>",
|
||||
params.executable.to_string_lossy()
|
||||
));
|
||||
};
|
||||
params.to = if let Some(to) = to {
|
||||
to
|
||||
} else if let Some(param) = opts.next() {
|
||||
param
|
||||
} else {
|
||||
return Err(format!("Usage: {} <from> <to>", exe.to_string_lossy()));
|
||||
return Err(format!(
|
||||
"Usage: {} <from> <to>",
|
||||
params.executable.to_string_lossy()
|
||||
));
|
||||
};
|
||||
|
||||
// diff DIRECTORY FILE => diff DIRECTORY/FILE FILE
|
||||
// diff FILE DIRECTORY => diff FILE DIRECTORY/FILE
|
||||
let mut from_path: PathBuf = PathBuf::from(¶ms.from);
|
||||
let mut to_path: PathBuf = PathBuf::from(¶ms.to);
|
||||
|
||||
if from_path.is_dir() && to_path.is_file() {
|
||||
from_path.push(to_path.file_name().unwrap());
|
||||
params.from = from_path.into_os_string();
|
||||
} else if from_path.is_file() && to_path.is_dir() {
|
||||
to_path.push(from_path.file_name().unwrap());
|
||||
params.to = to_path.into_os_string();
|
||||
}
|
||||
|
||||
params.format = format.unwrap_or(Format::default());
|
||||
if let Some(context_count) = context {
|
||||
params.context_count = context_count;
|
||||
}
|
||||
Ok(params)
|
||||
}
|
||||
|
||||
struct DiffStyleMatch {
|
||||
is_match: bool,
|
||||
context_count: Option<usize>,
|
||||
next_param_consumed: bool,
|
||||
}
|
||||
|
||||
fn match_context_diff_params(
|
||||
param: &OsString,
|
||||
next_param: Option<&OsString>,
|
||||
format: Option<Format>,
|
||||
) -> Result<DiffStyleMatch, String> {
|
||||
const CONTEXT_RE: &str = r"^(-[cC](?<num1>\d*)|--context(=(?<num2>\d*))?|-(?<num3>\d+)c)$";
|
||||
let regex = Regex::new(CONTEXT_RE).unwrap();
|
||||
let is_match = regex.is_match(param.to_string_lossy().as_ref());
|
||||
let mut context_count = None;
|
||||
let mut next_param_consumed = false;
|
||||
if is_match {
|
||||
if format.is_some() && format != Some(Format::Context) {
|
||||
return Err("Conflicting output style options".to_string());
|
||||
}
|
||||
let captures = regex.captures(param.to_str().unwrap()).unwrap();
|
||||
let num = captures
|
||||
.name("num1")
|
||||
.or(captures.name("num2"))
|
||||
.or(captures.name("num3"));
|
||||
if let Some(numvalue) = num {
|
||||
if !numvalue.as_str().is_empty() {
|
||||
context_count = Some(numvalue.as_str().parse::<usize>().unwrap());
|
||||
}
|
||||
}
|
||||
if param == "-C" {
|
||||
if let Some(p) = next_param {
|
||||
let size_str = p.to_string_lossy();
|
||||
match size_str.parse::<usize>() {
|
||||
Ok(context_size) => {
|
||||
context_count = Some(context_size);
|
||||
next_param_consumed = true;
|
||||
}
|
||||
Err(_) => return Err(format!("invalid context length '{size_str}'")),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(DiffStyleMatch {
|
||||
is_match,
|
||||
context_count,
|
||||
next_param_consumed,
|
||||
})
|
||||
}
|
||||
|
||||
fn match_unified_diff_params(
|
||||
param: &OsString,
|
||||
next_param: Option<&OsString>,
|
||||
format: Option<Format>,
|
||||
) -> Result<DiffStyleMatch, String> {
|
||||
const UNIFIED_RE: &str = r"^(-[uU](?<num1>\d*)|--unified(=(?<num2>\d*))?|-(?<num3>\d+)u)$";
|
||||
let regex = Regex::new(UNIFIED_RE).unwrap();
|
||||
let is_match = regex.is_match(param.to_string_lossy().as_ref());
|
||||
let mut context_count = None;
|
||||
let mut next_param_consumed = false;
|
||||
if is_match {
|
||||
if format.is_some() && format != Some(Format::Unified) {
|
||||
return Err("Conflicting output style options".to_string());
|
||||
}
|
||||
let captures = regex.captures(param.to_str().unwrap()).unwrap();
|
||||
let num = captures
|
||||
.name("num1")
|
||||
.or(captures.name("num2"))
|
||||
.or(captures.name("num3"));
|
||||
if let Some(numvalue) = num {
|
||||
if !numvalue.as_str().is_empty() {
|
||||
context_count = Some(numvalue.as_str().parse::<usize>().unwrap());
|
||||
}
|
||||
}
|
||||
if param == "-U" {
|
||||
if let Some(p) = next_param {
|
||||
let size_str = p.to_string_lossy();
|
||||
match size_str.parse::<usize>() {
|
||||
Ok(context_size) => {
|
||||
context_count = Some(context_size);
|
||||
next_param_consumed = true;
|
||||
}
|
||||
Err(_) => return Err(format!("invalid context length '{size_str}'")),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(DiffStyleMatch {
|
||||
is_match,
|
||||
context_count,
|
||||
next_param_consumed,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -192,29 +353,176 @@ mod tests {
|
||||
fn basics() {
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(
|
||||
[os("diff"), os("--normal"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn basics_ed() {
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
format: Format::Ed,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("-e"), os("foo"), os("bar")].iter().cloned())
|
||||
);
|
||||
for arg in ["-e", "--ed"] {
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
format: Format::Ed,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(
|
||||
[os("diff"), os(arg), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn context_valid() {
|
||||
for args in [vec!["-c"], vec!["--context"], vec!["--context="]] {
|
||||
let mut params = vec!["diff"];
|
||||
params.extend(args);
|
||||
params.extend(["foo", "bar"]);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
format: Format::Context,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(params.iter().map(|x| os(x)).peekable())
|
||||
);
|
||||
}
|
||||
for args in [
|
||||
vec!["-c42"],
|
||||
vec!["-C42"],
|
||||
vec!["-C", "42"],
|
||||
vec!["--context=42"],
|
||||
vec!["-42c"],
|
||||
] {
|
||||
let mut params = vec!["diff"];
|
||||
params.extend(args);
|
||||
params.extend(["foo", "bar"]);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
format: Format::Context,
|
||||
context_count: 42,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(params.iter().map(|x| os(x)).peekable())
|
||||
);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn context_invalid() {
|
||||
for args in [
|
||||
vec!["-c", "42"],
|
||||
vec!["-c=42"],
|
||||
vec!["-c="],
|
||||
vec!["-C"],
|
||||
vec!["-C=42"],
|
||||
vec!["-C="],
|
||||
vec!["--context42"],
|
||||
vec!["--context", "42"],
|
||||
vec!["-42C"],
|
||||
] {
|
||||
let mut params = vec!["diff"];
|
||||
params.extend(args);
|
||||
params.extend(["foo", "bar"]);
|
||||
assert!(parse_params(params.iter().map(|x| os(x)).peekable()).is_err());
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn unified_valid() {
|
||||
for args in [vec!["-u"], vec!["--unified"], vec!["--unified="]] {
|
||||
let mut params = vec!["diff"];
|
||||
params.extend(args);
|
||||
params.extend(["foo", "bar"]);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
format: Format::Unified,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(params.iter().map(|x| os(x)).peekable())
|
||||
);
|
||||
}
|
||||
for args in [
|
||||
vec!["-u42"],
|
||||
vec!["-U42"],
|
||||
vec!["-U", "42"],
|
||||
vec!["--unified=42"],
|
||||
vec!["-42u"],
|
||||
] {
|
||||
let mut params = vec!["diff"];
|
||||
params.extend(args);
|
||||
params.extend(["foo", "bar"]);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
format: Format::Unified,
|
||||
context_count: 42,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(params.iter().map(|x| os(x)).peekable())
|
||||
);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn unified_invalid() {
|
||||
for args in [
|
||||
vec!["-u", "42"],
|
||||
vec!["-u=42"],
|
||||
vec!["-u="],
|
||||
vec!["-U"],
|
||||
vec!["-U=42"],
|
||||
vec!["-U="],
|
||||
vec!["--unified42"],
|
||||
vec!["--unified", "42"],
|
||||
vec!["-42U"],
|
||||
] {
|
||||
let mut params = vec!["diff"];
|
||||
params.extend(args);
|
||||
params.extend(["foo", "bar"]);
|
||||
assert!(parse_params(params.iter().map(|x| os(x)).peekable()).is_err());
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn context_count() {
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
format: Format::Unified,
|
||||
@@ -225,10 +533,12 @@ mod tests {
|
||||
[os("diff"), os("-u54"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
format: Format::Unified,
|
||||
@@ -239,10 +549,12 @@ mod tests {
|
||||
[os("diff"), os("-U54"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
format: Format::Unified,
|
||||
@@ -253,10 +565,12 @@ mod tests {
|
||||
[os("diff"), os("-U"), os("54"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
format: Format::Context,
|
||||
@@ -267,6 +581,7 @@ mod tests {
|
||||
[os("diff"), os("-c54"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -274,23 +589,36 @@ mod tests {
|
||||
fn report_identical_files() {
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
report_identical_files: true,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("-s"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("-s"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
report_identical_files: true,
|
||||
@@ -305,6 +633,7 @@ mod tests {
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -312,23 +641,36 @@ mod tests {
|
||||
fn brief() {
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
brief: true,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("-q"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("-q"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
brief: true,
|
||||
@@ -338,6 +680,7 @@ mod tests {
|
||||
[os("diff"), os("--brief"), os("foo"), os("bar"),]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -345,15 +688,22 @@ mod tests {
|
||||
fn expand_tabs() {
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
for option in ["-t", "--expand-tabs"] {
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
expand_tabs: true,
|
||||
@@ -363,6 +713,7 @@ mod tests {
|
||||
[os("diff"), os(option), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -371,27 +722,36 @@ mod tests {
|
||||
fn tabsize() {
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
tabsize: 0,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(
|
||||
[os("diff"), os("--tabsize=0"), os("foo"), os("bar")]
|
||||
[os("diff"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
tabsize: 1,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(
|
||||
[os("diff"), os("--tabsize=1"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("bar"),
|
||||
tabsize: 42,
|
||||
@@ -401,36 +761,42 @@ mod tests {
|
||||
[os("diff"), os("--tabsize=42"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("--tabsize"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("--tabsize="), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("--tabsize=r2"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("--tabsize=-1"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("--tabsize=r2"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
@@ -442,6 +808,7 @@ mod tests {
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
@@ -449,57 +816,104 @@ mod tests {
|
||||
fn double_dash() {
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("-g"),
|
||||
to: os("-h"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("--"), os("-g"), os("-h")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("--"), os("-g"), os("-h")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn default_to_stdin() {
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
executable: os("diff"),
|
||||
from: os("foo"),
|
||||
to: os("/dev/stdin"),
|
||||
to: os("-"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("-")].iter().cloned())
|
||||
parse_params([os("diff"), os("foo"), os("-")].iter().cloned().peekable())
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
from: os("/dev/stdin"),
|
||||
executable: os("diff"),
|
||||
from: os("-"),
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("-"), os("bar")].iter().cloned())
|
||||
parse_params([os("diff"), os("-"), os("bar")].iter().cloned().peekable())
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
from: os("/dev/stdin"),
|
||||
to: os("/dev/stdin"),
|
||||
executable: os("diff"),
|
||||
from: os("-"),
|
||||
to: os("-"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("-"), os("-")].iter().cloned())
|
||||
parse_params([os("diff"), os("-"), os("-")].iter().cloned().peekable())
|
||||
);
|
||||
assert!(parse_params([os("diff"), os("foo"), os("bar"), os("-")].iter().cloned()).is_err());
|
||||
assert!(parse_params([os("diff"), os("-"), os("-"), os("-")].iter().cloned()).is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("foo"), os("bar"), os("-")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("-"), os("-"), os("-")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
#[test]
|
||||
fn missing_arguments() {
|
||||
assert!(parse_params([os("diff")].iter().cloned()).is_err());
|
||||
assert!(parse_params([os("diff"), os("foo")].iter().cloned()).is_err());
|
||||
assert!(parse_params([os("diff")].iter().cloned().peekable()).is_err());
|
||||
assert!(parse_params([os("diff"), os("foo")].iter().cloned().peekable()).is_err());
|
||||
}
|
||||
#[test]
|
||||
fn unknown_argument() {
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("-g"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(
|
||||
parse_params([os("diff"), os("-g"), os("foo"), os("bar")].iter().cloned()).is_err()
|
||||
parse_params([os("diff"), os("-g"), os("bar")].iter().cloned().peekable()).is_err()
|
||||
);
|
||||
assert!(parse_params([os("diff"), os("-g"), os("bar")].iter().cloned()).is_err());
|
||||
assert!(parse_params([os("diff"), os("-g")].iter().cloned()).is_err());
|
||||
assert!(parse_params([os("diff"), os("-g")].iter().cloned().peekable()).is_err());
|
||||
}
|
||||
#[test]
|
||||
fn empty() {
|
||||
assert!(parse_params([].iter().cloned()).is_err());
|
||||
assert!(parse_params([].iter().cloned().peekable()).is_err());
|
||||
}
|
||||
#[test]
|
||||
fn conflicting_output_styles() {
|
||||
for (arg1, arg2) in [
|
||||
("-u", "-c"),
|
||||
("-u", "-e"),
|
||||
("-c", "-u"),
|
||||
("-c", "-U42"),
|
||||
("-u", "--normal"),
|
||||
("--normal", "-e"),
|
||||
("--context", "--normal"),
|
||||
] {
|
||||
assert!(parse_params(
|
||||
[os("diff"), os(arg1), os(arg2), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1263
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::utils::do_write_line;
|
||||
use crate::utils::get_modification_time;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DiffLine {
|
||||
@@ -238,10 +239,14 @@ fn make_diff(
|
||||
|
||||
#[must_use]
|
||||
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!(
|
||||
"--- {0}\t\n+++ {1}\t\n",
|
||||
"--- {0}\t{1}\n+++ {2}\t{3}\n",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy()
|
||||
from_modified_time,
|
||||
params.to.to_string_lossy(),
|
||||
to_modified_time
|
||||
)
|
||||
.into_bytes();
|
||||
let diff_results = make_diff(expected, actual, params.context_count, params.brief);
|
||||
@@ -461,13 +466,13 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
File::create(&format!("{target}/ab.diff"))
|
||||
File::create(format!("{target}/ab.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alef")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
@@ -489,13 +494,13 @@ mod tests {
|
||||
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.stdin(File::open(&format!("{target}/ab.diff")).unwrap())
|
||||
.stdin(File::open(format!("{target}/ab.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -577,25 +582,25 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
File::create(&format!("{target}/abn.diff"))
|
||||
File::create(format!("{target}/abn.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alefn")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alefn")).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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.stdin(File::open(&format!("{target}/abn.diff")).unwrap())
|
||||
.stdin(File::open(format!("{target}/abn.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//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);
|
||||
}
|
||||
}
|
||||
@@ -673,25 +678,25 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
File::create(&format!("{target}/ab_.diff"))
|
||||
File::create(format!("{target}/ab_.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alef_")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.stdin(File::open(&format!("{target}/ab_.diff")).unwrap())
|
||||
.stdin(File::open(format!("{target}/ab_.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//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);
|
||||
}
|
||||
}
|
||||
@@ -754,25 +759,25 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
File::create(&format!("{target}/abx.diff"))
|
||||
File::create(format!("{target}/abx.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alefx")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alefx")).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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.stdin(File::open(&format!("{target}/abx.diff")).unwrap())
|
||||
.stdin(File::open(format!("{target}/abx.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//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);
|
||||
}
|
||||
}
|
||||
@@ -840,25 +845,25 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
File::create(&format!("{target}/abr.diff"))
|
||||
File::create(format!("{target}/abr.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alefr")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alefr")).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();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.stdin(File::open(&format!("{target}/abr.diff")).unwrap())
|
||||
.stdin(File::open(format!("{target}/abr.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//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);
|
||||
}
|
||||
}
|
||||
@@ -870,6 +875,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_stop_early() {
|
||||
use crate::assert_diff_eq;
|
||||
|
||||
let from_filename = "foo";
|
||||
let from = ["a", "b", "c", ""].join("\n");
|
||||
let to_filename = "bar";
|
||||
@@ -884,9 +891,10 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let expected_full = [
|
||||
"--- foo\t",
|
||||
"+++ bar\t",
|
||||
"--- foo\tTIMESTAMP",
|
||||
"+++ bar\tTIMESTAMP",
|
||||
"@@ -1,3 +1,3 @@",
|
||||
" a",
|
||||
"-b",
|
||||
@@ -895,7 +903,7 @@ mod tests {
|
||||
"",
|
||||
]
|
||||
.join("\n");
|
||||
assert_eq!(diff_full, expected_full.as_bytes());
|
||||
assert_diff_eq!(diff_full, expected_full);
|
||||
|
||||
let diff_brief = diff(
|
||||
from.as_bytes(),
|
||||
@@ -907,8 +915,9 @@ mod tests {
|
||||
..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(
|
||||
from.as_bytes(),
|
||||
|
||||
+95
-6
@@ -3,8 +3,8 @@
|
||||
// For the full copyright and license information, please view the LICENSE-*
|
||||
// files that was distributed with this source code.
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
use regex::Regex;
|
||||
use std::{ffi::OsString, io::Write};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
/// Replace tabs by spaces in the input line.
|
||||
@@ -52,6 +52,52 @@ 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
|
||||
}
|
||||
|
||||
pub fn format_failure_to_read_input_file(
|
||||
executable: &OsString,
|
||||
filepath: &OsString,
|
||||
error: &std::io::Error,
|
||||
) -> String {
|
||||
// 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();
|
||||
format!(
|
||||
"{}: {}: {}",
|
||||
executable.to_string_lossy(),
|
||||
filepath.to_string_lossy(),
|
||||
error_code_re.replace(error.to_string().as_str(), ""),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn report_failure_to_read_input_file(
|
||||
executable: &OsString,
|
||||
filepath: &OsString,
|
||||
error: &std::io::Error,
|
||||
) {
|
||||
eprintln!(
|
||||
"{}",
|
||||
format_failure_to_read_input_file(executable, filepath, error)
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -82,10 +128,11 @@ mod tests {
|
||||
|
||||
// Note: The Woman Scientist emoji (👩🔬) is a ZWJ sequence combining
|
||||
// the Woman emoji (👩) and the Microscope emoji (🔬). On supported platforms
|
||||
// it is displayed as a single emoji and should have a print size of 2 columns,
|
||||
// but terminal emulators tend to not support this, and display the two emojis
|
||||
// side by side, thus accounting for a print size of 4 columns.
|
||||
assert_tab_expansion("foo\t👩🔬\tbaz", 6, "foo 👩🔬 baz");
|
||||
// it is displayed as a single emoji and has a print size of 2 columns.
|
||||
// Terminal emulators tend to not support this, and display the two emojis
|
||||
// side by side, thus accounting for a print size of 4 columns, but the
|
||||
// unicode_width crate reports a correct size of 2.
|
||||
assert_tab_expansion("foo\t👩🔬\tbaz", 6, "foo 👩🔬 baz");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -115,4 +162,46 @@ mod tests {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+853
-168
File diff suppressed because it is too large
Load Diff
@@ -19,9 +19,9 @@
|
||||
# By default it expects a release build of the diffutils binary, but a
|
||||
# different build profile can be specified as an argument
|
||||
# (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
|
||||
# (e.g. cmp, diff3 or sdiff) are skipped.
|
||||
# (e.g. diff3 or sdiff) are skipped.
|
||||
|
||||
scriptpath=$(dirname "$(readlink -f "$0")")
|
||||
rev=$(git rev-parse HEAD)
|
||||
@@ -57,8 +57,13 @@ upstreamrev=$(git rev-parse HEAD)
|
||||
mkdir src
|
||||
cd src
|
||||
ln -s "$binary" diff
|
||||
ln -s "$binary" cmp
|
||||
cd ../tests
|
||||
|
||||
# Fetch tests/init.sh from the gnulib repository (needed since
|
||||
# https://git.savannah.gnu.org/cgit/diffutils.git/commit/tests?id=1d2456f539)
|
||||
curl -s "$gitserver/gitweb/?p=gnulib.git;a=blob_plain;f=tests/init.sh;hb=HEAD" -o init.sh
|
||||
|
||||
if [[ -n "$TESTS" ]]
|
||||
then
|
||||
tests="$TESTS"
|
||||
@@ -71,7 +76,6 @@ total=$(echo "$tests" | wc -w)
|
||||
echo "Running $total tests"
|
||||
export LC_ALL=C
|
||||
export KEEP=yes
|
||||
exitcode=0
|
||||
timestamp=$(date -Iseconds)
|
||||
urlroot="$gitserver/cgit/diffutils.git/tree/tests/"
|
||||
passed=0
|
||||
@@ -82,35 +86,43 @@ for test in $tests
|
||||
do
|
||||
result="FAIL"
|
||||
url="$urlroot$test?id=$upstreamrev"
|
||||
# Run only the tests that invoke `diff`,
|
||||
# Run only the tests that invoke `diff` or `cmp`,
|
||||
# because other binaries aren't implemented yet
|
||||
if ! grep -E -s -q "(cmp|diff3|sdiff)" "$test"
|
||||
if ! grep -E -s -q "(diff3|sdiff)" "$test"
|
||||
then
|
||||
sh "$test" 1> stdout.txt 2> stderr.txt && result="PASS" || exitcode=1
|
||||
json+="{\"test\":\"$test\",\"result\":\"$result\","
|
||||
json+="\"url\":\"$url\","
|
||||
json+="\"stdout\":\"$(base64 -w0 < stdout.txt)\","
|
||||
json+="\"stderr\":\"$(base64 -w0 < stderr.txt)\","
|
||||
json+="\"files\":{"
|
||||
cd gt-$test.*
|
||||
# Note: this doesn't include the contents of subdirectories,
|
||||
# but there isn't much value added in doing so
|
||||
for file in *
|
||||
do
|
||||
[[ -f "$file" ]] && json+="\"$file\":\"$(base64 -w0 < "$file")\","
|
||||
done
|
||||
json="${json%,}}},"
|
||||
cd - > /dev/null
|
||||
[[ "$result" = "PASS" ]] && (( passed++ ))
|
||||
[[ "$result" = "FAIL" ]] && (( failed++ ))
|
||||
sh "$test" 1> stdout.txt 2> stderr.txt && result="PASS"
|
||||
if [[ $? = 77 ]]
|
||||
then
|
||||
result="SKIP"
|
||||
else
|
||||
json+="{\"test\":\"$test\",\"result\":\"$result\","
|
||||
json+="\"url\":\"$url\","
|
||||
json+="\"stdout\":\"$(base64 -w0 < stdout.txt)\","
|
||||
json+="\"stderr\":\"$(base64 -w0 < stderr.txt)\","
|
||||
json+="\"files\":{"
|
||||
cd gt-$test.*
|
||||
# Note: this doesn't include the contents of subdirectories,
|
||||
# but there isn't much value added in doing so
|
||||
for file in *
|
||||
do
|
||||
[[ -f "$file" ]] && json+="\"$file\":\"$(base64 -w0 < "$file")\","
|
||||
done
|
||||
json="${json%,}}},"
|
||||
cd - > /dev/null
|
||||
[[ "$result" = "PASS" ]] && (( passed++ ))
|
||||
[[ "$result" = "FAIL" ]] && (( failed++ ))
|
||||
fi
|
||||
else
|
||||
result="SKIP"
|
||||
(( skipped++ ))
|
||||
json+="{\"test\":\"$test\",\"url\":\"$url\",\"result\":\"$result\"},"
|
||||
fi
|
||||
color=2 # green
|
||||
[[ "$result" = "FAIL" ]] && color=1 # red
|
||||
[[ "$result" = "SKIP" ]] && color=3 # yellow
|
||||
if [[ $result = "SKIP" ]]
|
||||
then
|
||||
(( skipped++ ))
|
||||
json+="{\"test\":\"$test\",\"url\":\"$url\",\"result\":\"$result\"},"
|
||||
color=3 # yellow
|
||||
fi
|
||||
printf " %-40s $(tput setaf $color)$result$(tput sgr0)\n" "$test"
|
||||
done
|
||||
echo ""
|
||||
@@ -138,4 +150,5 @@ resultsfile="test-results.json"
|
||||
echo "$json" | jq > "$resultsfile"
|
||||
echo "Results written to $scriptpath/$resultsfile"
|
||||
|
||||
exit $exitcode
|
||||
(( failed > 0 )) && exit 1
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user