diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 977a86915..5ac9295d4 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -11,7 +11,7 @@ env: PROJECT_NAME: coreutils PROJECT_DESC: "Core universal (cross-platform) utilities" PROJECT_AUTH: "uutils" - RUST_MIN_SRV: "1.40.0" ## v1.40.0 + RUST_MIN_SRV: "1.43.1" ## v1.43.0 RUST_COV_SRV: "2020-08-01" ## (~v1.47.0) supported rust version for code coverage; (date required/used by 'coverage') ## !maint: refactor when code coverage support is included in the stable channel on: [push, pull_request] @@ -84,7 +84,7 @@ jobs: - name: Install `rust` toolchain uses: actions-rs/toolchain@v1 with: - toolchain: stable + toolchain: nightly default: true profile: minimal # minimal component installation (ie, no documentation) components: clippy @@ -94,7 +94,7 @@ jobs: run: | # `clippy` testing # * convert any warnings to GHA UI annotations; ref: - S=$(cargo clippy ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} -- -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::warning file=\2,line=\3,col=\4::WARNING: \`cargo clippy\`: \1/p;" -e '}' ; } + S=$(cargo +nightly clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} -- -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::warning file=\2,line=\3,col=\4::WARNING: \`cargo clippy\`: \1/p;" -e '}' ; } min_version: name: MinRustV # Minimum supported rust version @@ -614,3 +614,20 @@ jobs: flags: ${{ steps.vars.outputs.CODECOV_FLAGS }} name: codecov-umbrella fail_ci_if_error: false + spellcheck: + name: Spell Check + runs-on: ${{ matrix.job.os }} + strategy: + matrix: + job: + - { os: ubuntu-latest } + steps: + - uses: actions/checkout@v1 + - name: Install/setup prerequisites + shell: bash + run: | + sudo apt-get -y update ; sudo apt-get -y install npm ; sudo npm install cspell -g; + - name: Run `cspell` + shell: bash + run: | + cspell --config .vscode/cSpell.json --no-summary --no-progress $( git ls-files | grep "\.\(rs\|md\)" ) | sed "s/\(.*\):\(.*\):\(.*\) - \(.*\)/::warning file=\1,line=\2,col=\3::cspell: \4/" || true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 66d2a5f5a..e3ad98ee3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,8 +1,17 @@ -# https://pre-commit.com repos: - - repo: https://github.com/doublify/pre-commit-rust - rev: v1.0 +- repo: local hooks: - - id: cargo-check - - id: clippy - - id: fmt + - id: rust-linting + name: Rust linting + description: Run cargo fmt on files included in the commit. + entry: cargo +nightly fmt -- + pass_filenames: true + types: [file, rust] + language: system + - id: rust-clippy + name: Rust clippy + description: Run cargo clippy on files included in the commit. + entry: cargo +nightly clippy --all-targets --all-features -- + pass_filenames: false + types: [file, rust] + language: system diff --git a/Cargo.lock b/Cargo.lock index 5e1470c88..1365049a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "Inflector" version = "0.11.4" @@ -43,12 +45,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "array-init" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6945cc5422176fc5e602e590c2878d2c2acd9a4fe20a4baa7c28022521698ec6" - [[package]] name = "arrayvec" version = "0.4.12" @@ -134,15 +130,8 @@ dependencies = [ "lazy_static", "memchr 2.4.0", "regex-automata", - "serde", ] -[[package]] -name = "bumpalo" -version = "3.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" - [[package]] name = "byte-tools" version = "0.2.0" @@ -155,15 +144,6 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "cast" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57cdfa5d50aad6cb4d44dcab6101a7f79925bd59d82ca42f38a9856a28865374" -dependencies = [ - "rustc_version", -] - [[package]] name = "cc" version = "1.0.67" @@ -244,6 +224,7 @@ dependencies = [ name = "coreutils" version = "0.0.6" dependencies = [ + "atty", "conv", "filetime", "glob 0.3.0", @@ -254,7 +235,6 @@ dependencies = [ "rand 0.7.3", "regex", "sha1", - "tempdir", "tempfile", "textwrap", "time", @@ -285,7 +265,6 @@ dependencies = [ "uu_expand", "uu_expr", "uu_factor", - "uu_factor_benches", "uu_false", "uu_fmt", "uu_fold", @@ -464,42 +443,6 @@ dependencies = [ "unicode-xid 0.0.4", ] -[[package]] -name = "criterion" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23" -dependencies = [ - "atty", - "cast", - "clap", - "criterion-plot", - "csv", - "itertools 0.10.0", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_cbor", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d" -dependencies = [ - "cast", - "itertools 0.9.0", -] - [[package]] name = "crossbeam-channel" version = "0.5.1" @@ -545,6 +488,31 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crossterm" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c36c10130df424b2f3552fcc2ddcd9b28a27b1e54b358b45874f88d1ca6888c" +dependencies = [ + "bitflags", + "crossterm_winapi", + "lazy_static", + "libc", + "mio", + "parking_lot", + "signal-hook", + "winapi 0.3.9", +] + +[[package]] +name = "crossterm_winapi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da8964ace4d3e4a044fd027919b2237000b24315a37c916f61809f1ff2140b9" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "csv" version = "1.1.6" @@ -707,7 +675,18 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1", ] [[package]] @@ -782,6 +761,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46dbcb333e86939721589d25a3557e180b52778cb33c7fdfe9e0158ff790d5ec" +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "ioctl-sys" version = "0.5.2" @@ -797,15 +785,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.10.0" @@ -815,21 +794,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" - -[[package]] -name = "js-sys" -version = "0.3.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "kernel32-sys" version = "0.2.2" @@ -861,6 +825,15 @@ dependencies = [ "libc", ] +[[package]] +name = "lock_api" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.14" @@ -921,6 +894,28 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mio" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e50ae3f04d169fcc9bde0b547d1c205219b7157e07ded9c5aff03e0637cb3ed7" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi 0.3.9", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "nix" version = "0.13.1" @@ -952,6 +947,26 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi 0.3.9", + ] + +[[package]] +name = "num-bigint" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -1021,12 +1036,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - [[package]] name = "ouroboros" version = "0.9.3" @@ -1059,6 +1068,31 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall 0.2.8", + "smallvec 1.6.1", + "winapi 0.3.9", +] + [[package]] name = "paste" version = "0.1.18" @@ -1078,15 +1112,6 @@ dependencies = [ "proc-macro-hack", ] -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - [[package]] name = "pkg-config" version = "0.3.19" @@ -1103,34 +1128,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "plotters" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07fffcddc1cb3a1de753caa4e4df03b79922ba43cf882acc1bdd7e8df9f4590" - -[[package]] -name = "plotters-svg" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b38a02e23bd9604b842a812063aec4ef702b57989c37b655254bb61c471ad211" -dependencies = [ - "plotters-backend", -] - [[package]] name = "ppv-lite86" version = "0.2.10" @@ -1221,19 +1218,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -dependencies = [ - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "rdrand", - "winapi 0.3.9", -] - [[package]] name = "rand" version = "0.5.6" @@ -1253,14 +1237,26 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.16", "libc", - "rand_chacha", + "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc", + "rand_hc 0.2.0", "rand_pcg", ] +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha 0.3.0", + "rand_core 0.6.2", + "rand_hc 0.3.0", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -1271,6 +1267,16 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.2", +] + [[package]] name = "rand_core" version = "0.3.1" @@ -1292,7 +1298,16 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom 0.2.3", ] [[package]] @@ -1304,6 +1319,15 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core 0.6.2", +] + [[package]] name = "rand_pcg" version = "0.2.1" @@ -1338,15 +1362,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "redox_syscall" version = "0.1.57" @@ -1418,21 +1433,6 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] - -[[package]] -name = "ryu" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" - [[package]] name = "same-file" version = "1.0.6" @@ -1454,16 +1454,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser 0.7.0", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser 0.10.2", + "semver-parser", ] [[package]] @@ -1472,53 +1463,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - -[[package]] -name = "serde" -version = "1.0.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" - -[[package]] -name = "serde_cbor" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" -dependencies = [ - "half", - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" -dependencies = [ - "proc-macro2", - "quote 1.0.9", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" -dependencies = [ - "itoa", - "ryu", - "serde", -] - [[package]] name = "sha1" version = "0.6.0" @@ -1550,6 +1494,26 @@ dependencies = [ "generic-array", ] +[[package]] +name = "signal-hook" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" +dependencies = [ + "libc", + "mio", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +dependencies = [ + "libc", +] + [[package]] name = "smallvec" version = "0.6.14" @@ -1559,6 +1523,12 @@ dependencies = [ "maybe-uninit", ] +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + [[package]] name = "socket2" version = "0.3.19" @@ -1611,26 +1581,16 @@ dependencies = [ "unicode-xid 0.2.2", ] -[[package]] -name = "tempdir" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -dependencies = [ - "rand 0.4.6", - "remove_dir_all", -] - [[package]] name = "tempfile" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "libc", - "rand 0.7.3", - "redox_syscall 0.1.57", + "rand 0.8.3", + "redox_syscall 0.2.8", "remove_dir_all", "winapi 0.3.9", ] @@ -1719,28 +1679,12 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "typenum" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" -[[package]] -name = "ucd-trie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" - [[package]] name = "unicode-segmentation" version = "1.7.1" @@ -2027,6 +1971,8 @@ name = "uu_expr" version = "0.0.6" dependencies = [ "libc", + "num-bigint", + "num-traits", "onig", "uucore", "uucore_procs", @@ -2036,27 +1982,17 @@ dependencies = [ name = "uu_factor" version = "0.0.6" dependencies = [ + "clap", "coz", "num-traits", "paste", "quickcheck", "rand 0.7.3", - "smallvec", + "smallvec 0.6.14", "uucore", "uucore_procs", ] -[[package]] -name = "uu_factor_benches" -version = "0.0.0" -dependencies = [ - "array-init", - "criterion", - "rand 0.7.3", - "rand_chacha", - "uu_factor", -] - [[package]] name = "uu_false" version = "0.0.6" @@ -2279,7 +2215,9 @@ dependencies = [ name = "uu_more" version = "0.0.6" dependencies = [ + "atty", "clap", + "crossterm", "nix 0.13.1", "redox_syscall 0.1.57", "redox_termios", @@ -2535,8 +2473,8 @@ dependencies = [ "ouroboros", "rand 0.7.3", "rayon", - "semver 0.9.0", - "tempdir", + "semver", + "tempfile", "unicode-width", "uucore", "uucore_procs", @@ -2878,68 +2816,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] -name = "wasm-bindgen" -version = "0.2.74" +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" -dependencies = [ - "cfg-if 1.0.0", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote 1.0.9", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" -dependencies = [ - "quote 1.0.9", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" -dependencies = [ - "proc-macro2", - "quote 1.0.9", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" - -[[package]] -name = "web-sys" -version = "0.3.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" -dependencies = [ - "js-sys", - "wasm-bindgen", -] +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wild" diff --git a/Cargo.toml b/Cargo.toml index 745393260..3412ff9f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -325,7 +325,8 @@ who = { optional=true, version="0.0.6", package="uu_who", path="src/uu/who" whoami = { optional=true, version="0.0.6", package="uu_whoami", path="src/uu/whoami" } yes = { optional=true, version="0.0.6", package="uu_yes", path="src/uu/yes" } -factor_benches = { optional = true, version = "0.0.0", package = "uu_factor_benches", path = "tests/benches/factor" } +# this breaks clippy linting with: "tests/by-util/test_factor_benches.rs: No such file or directory (os error 2)" +# factor_benches = { optional = true, version = "0.0.0", package = "uu_factor_benches", path = "tests/benches/factor" } # # * pinned transitive dependencies @@ -342,14 +343,12 @@ pretty_assertions = "0.7.2" rand = "0.7" regex = "1.0" sha1 = { version="0.6", features=["std"] } -## tempfile 3.2 depends on recent version of rand which depends on getrandom v0.2 which has compiler errors for MinRustV v1.32.0 -## min dep for tempfile = Rustc 1.40 -tempfile = "= 3.1.0" +tempfile = "3.2.0" time = "0.1" unindent = "0.1" uucore = { version=">=0.0.8", package="uucore", path="src/uucore", features=["entries"] } walkdir = "2.2" -tempdir = "0.3" +atty = "0.2.14" [target.'cfg(unix)'.dev-dependencies] rust-users = { version="0.10", package="users" } diff --git a/DEVELOPER_INSTRUCTIONS.md b/DEVELOPER_INSTRUCTIONS.md index c3b20dd46..3aa8b5b12 100644 --- a/DEVELOPER_INSTRUCTIONS.md +++ b/DEVELOPER_INSTRUCTIONS.md @@ -21,7 +21,7 @@ if changes are not reflected in the report then run `cargo clean` and run the a ### Using Stable Rust -If you are using stable version of Rust that doesn't enable code coverage instrumentation by default +If you are using stable version of Rust that doesn't enable code coverage instrumentation by default then add `-Z-Zinstrument-coverage` flag to `RUSTFLAGS` env variable specified above. @@ -36,3 +36,7 @@ To use the provided hook: 2. Run `pre-commit install` while in the repository directory Your git commits will then automatically be checked. If a check fails, an error message will explain why, and your commit will be canceled. You can then make the suggested changes, and run `git commit ...` again. + +### Using Clippy + +The `msrv` key in the clippy configuration file `clippy.toml` is used to disable lints pertaining to newer features by specifying the minimum supported Rust version (MSRV). However, this key is only supported on `nightly`. To invoke clippy without errors, use `cargo +nightly clippy`. In order to also check tests and non-default crate features, use `cargo +nightly clippy --all-targets --all-features`. diff --git a/README.md b/README.md index 6b29fa854..1365bf7ce 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ to compile anywhere, and this is as good a way as any to try and learn it. ### Rust Version uutils follows Rust's release channels and is tested against stable, beta and nightly. -The current oldest supported version of the Rust compiler is `1.40.0`. +The current oldest supported version of the Rust compiler is `1.43.1`. On both Windows and Redox, only the nightly version is tested currently. diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 000000000..0a0a69a41 --- /dev/null +++ b/clippy.toml @@ -0,0 +1 @@ +msrv = "1.43.1" diff --git a/src/uu/basename/src/basename.rs b/src/uu/basename/src/basename.rs index c20561b30..ebd69de79 100644 --- a/src/uu/basename/src/basename.rs +++ b/src/uu/basename/src/basename.rs @@ -136,7 +136,8 @@ fn basename(fullname: &str, suffix: &str) -> String { } } -#[allow(clippy::manual_strip)] // can be replaced with strip_suffix once the minimum rust version is 1.45 +// can be replaced with strip_suffix once MSRV is 1.45 +#[allow(clippy::manual_strip)] fn strip_suffix(name: &str, suffix: &str) -> String { if name == suffix { return name.to_owned(); diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 7e64a288c..4d9e5965e 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -232,6 +232,7 @@ fn get_usage() -> String { static OPT_ARCHIVE: &str = "archive"; static OPT_ATTRIBUTES_ONLY: &str = "attributes-only"; static OPT_BACKUP: &str = "backup"; +static OPT_BACKUP_NO_ARG: &str = "b"; static OPT_CLI_SYMBOLIC_LINKS: &str = "cli-symbolic-links"; static OPT_CONTEXT: &str = "context"; static OPT_COPY_CONTENTS: &str = "copy-contents"; @@ -357,7 +358,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .help("remove each existing destination file before attempting to open it \ (contrast with --force). On Windows, current only works for writeable files.")) .arg(Arg::with_name(OPT_BACKUP) - .short("b") .long(OPT_BACKUP) .help("make a backup of each existing destination file") .takes_value(true) @@ -366,6 +366,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .possible_values(backup_control::BACKUP_CONTROL_VALUES) .value_name("CONTROL") ) + .arg(Arg::with_name(OPT_BACKUP_NO_ARG) + .short(OPT_BACKUP_NO_ARG) + .help("like --backup but does not accept an argument") + ) .arg(Arg::with_name(OPT_SUFFIX) .short("S") .long(OPT_SUFFIX) @@ -592,7 +596,7 @@ impl Options { || matches.is_present(OPT_ARCHIVE); let backup_mode = backup_control::determine_backup_mode( - matches.is_present(OPT_BACKUP), + matches.is_present(OPT_BACKUP_NO_ARG) || matches.is_present(OPT_BACKUP), matches.value_of(OPT_BACKUP), ); let backup_suffix = backup_control::determine_backup_suffix(matches.value_of(OPT_SUFFIX)); @@ -665,8 +669,8 @@ impl Options { } }, backup: backup_mode, - backup_suffix: backup_suffix, - overwrite: overwrite, + backup_suffix, + overwrite, no_target_dir, preserve_attributes, recursive, @@ -1085,7 +1089,7 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu } #[cfg(not(windows))] -#[allow(clippy::unnecessary_wraps)] // needed for windows version +#[allow(clippy::unnecessary_unwrap)] // needed for windows version fn symlink_file(source: &Path, dest: &Path, context: &str) -> CopyResult<()> { match std::os::unix::fs::symlink(source, dest).context(context) { Ok(_) => Ok(()), @@ -1104,7 +1108,7 @@ fn context_for(src: &Path, dest: &Path) -> String { /// Implements a simple backup copy for the destination file. /// TODO: for the backup, should this function be replaced by `copy_file(...)`? -fn backup_dest(dest: &Path, backup_path: &PathBuf) -> CopyResult { +fn backup_dest(dest: &Path, backup_path: &Path) -> CopyResult { fs::copy(dest, &backup_path)?; Ok(backup_path.into()) } diff --git a/src/uu/csplit/src/csplit.rs b/src/uu/csplit/src/csplit.rs index 9d2f81f43..43f95fff5 100644 --- a/src/uu/csplit/src/csplit.rs +++ b/src/uu/csplit/src/csplit.rs @@ -124,12 +124,7 @@ where // split the file based on patterns for pattern in patterns.into_iter() { let pattern_as_str = pattern.to_string(); - #[allow(clippy::match_like_matches_macro)] - let is_skip = if let patterns::Pattern::SkipToMatch(_, _, _) = pattern { - true - } else { - false - }; + let is_skip = matches!(pattern, patterns::Pattern::SkipToMatch(_, _, _)); match pattern { patterns::Pattern::UpToLine(n, ex) => { let mut up_to_line = n; @@ -488,10 +483,11 @@ where /// Shrink the buffer so that its length is equal to the set size, returning an iterator for /// the elements that were too much. fn shrink_buffer_to_size(&mut self) -> impl Iterator + '_ { - let mut shrink_offset = 0; - if self.buffer.len() > self.size { - shrink_offset = self.buffer.len() - self.size; - } + let shrink_offset = if self.buffer.len() > self.size { + self.buffer.len() - self.size + } else { + 0 + }; self.buffer .drain(..shrink_offset) .map(|(_, line)| line.unwrap()) diff --git a/src/uu/expr/Cargo.toml b/src/uu/expr/Cargo.toml index c535df7ce..ed992bf71 100644 --- a/src/uu/expr/Cargo.toml +++ b/src/uu/expr/Cargo.toml @@ -16,6 +16,8 @@ path = "src/expr.rs" [dependencies] libc = "0.2.42" +num-bigint = "0.4.0" +num-traits = "0.2.14" onig = "~4.3.2" uucore = { version=">=0.0.8", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index a75f4c742..b72d78729 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -12,6 +12,8 @@ // spell-checker:ignore (ToDO) binop binops ints paren prec +use num_bigint::BigInt; +use num_traits::{One, Zero}; use onig::{Regex, RegexOptions, Syntax}; use crate::tokens::Token; @@ -39,20 +41,17 @@ impl AstNode { for _ in 0..depth { print!("\t",); } - match *self { - AstNode::Leaf { - ref token_idx, - ref value, - } => println!( + match self { + AstNode::Leaf { token_idx, value } => println!( "Leaf( {} ) at #{} ( evaluate -> {:?} )", value, token_idx, self.evaluate() ), AstNode::Node { - ref token_idx, - ref op_type, - ref operands, + token_idx, + op_type, + operands, } => { println!( "Node( {} ) at #{} (evaluate -> {:?})", @@ -81,36 +80,33 @@ impl AstNode { }) } pub fn evaluate(&self) -> Result { - match *self { - AstNode::Leaf { ref value, .. } => Ok(value.clone()), - AstNode::Node { ref op_type, .. } => match self.operand_values() { + match self { + AstNode::Leaf { value, .. } => Ok(value.clone()), + AstNode::Node { op_type, .. } => match self.operand_values() { Err(reason) => Err(reason), Ok(operand_values) => match op_type.as_ref() { - "+" => infix_operator_two_ints( - |a: i64, b: i64| checked_binop(|| a.checked_add(b), "+"), - &operand_values, - ), - "-" => infix_operator_two_ints( - |a: i64, b: i64| checked_binop(|| a.checked_sub(b), "-"), - &operand_values, - ), - "*" => infix_operator_two_ints( - |a: i64, b: i64| checked_binop(|| a.checked_mul(b), "*"), - &operand_values, - ), + "+" => { + infix_operator_two_ints(|a: BigInt, b: BigInt| Ok(a + b), &operand_values) + } + "-" => { + infix_operator_two_ints(|a: BigInt, b: BigInt| Ok(a - b), &operand_values) + } + "*" => { + infix_operator_two_ints(|a: BigInt, b: BigInt| Ok(a * b), &operand_values) + } "/" => infix_operator_two_ints( - |a: i64, b: i64| { - if b == 0 { + |a: BigInt, b: BigInt| { + if b.is_zero() { Err("division by zero".to_owned()) } else { - checked_binop(|| a.checked_div(b), "/") + Ok(a / b) } }, &operand_values, ), "%" => infix_operator_two_ints( - |a: i64, b: i64| { - if b == 0 { + |a: BigInt, b: BigInt| { + if b.is_zero() { Err("division by zero".to_owned()) } else { Ok(a % b) @@ -119,32 +115,32 @@ impl AstNode { &operand_values, ), "=" => infix_operator_two_ints_or_two_strings( - |a: i64, b: i64| Ok(bool_as_int(a == b)), + |a: BigInt, b: BigInt| Ok(bool_as_int(a == b)), |a: &String, b: &String| Ok(bool_as_string(a == b)), &operand_values, ), "!=" => infix_operator_two_ints_or_two_strings( - |a: i64, b: i64| Ok(bool_as_int(a != b)), + |a: BigInt, b: BigInt| Ok(bool_as_int(a != b)), |a: &String, b: &String| Ok(bool_as_string(a != b)), &operand_values, ), "<" => infix_operator_two_ints_or_two_strings( - |a: i64, b: i64| Ok(bool_as_int(a < b)), + |a: BigInt, b: BigInt| Ok(bool_as_int(a < b)), |a: &String, b: &String| Ok(bool_as_string(a < b)), &operand_values, ), ">" => infix_operator_two_ints_or_two_strings( - |a: i64, b: i64| Ok(bool_as_int(a > b)), + |a: BigInt, b: BigInt| Ok(bool_as_int(a > b)), |a: &String, b: &String| Ok(bool_as_string(a > b)), &operand_values, ), "<=" => infix_operator_two_ints_or_two_strings( - |a: i64, b: i64| Ok(bool_as_int(a <= b)), + |a: BigInt, b: BigInt| Ok(bool_as_int(a <= b)), |a: &String, b: &String| Ok(bool_as_string(a <= b)), &operand_values, ), ">=" => infix_operator_two_ints_or_two_strings( - |a: i64, b: i64| Ok(bool_as_int(a >= b)), + |a: BigInt, b: BigInt| Ok(bool_as_int(a >= b)), |a: &String, b: &String| Ok(bool_as_string(a >= b)), &operand_values, ), @@ -161,7 +157,7 @@ impl AstNode { } } pub fn operand_values(&self) -> Result, String> { - if let AstNode::Node { ref operands, .. } = *self { + if let AstNode::Node { operands, .. } = self { let mut out = Vec::with_capacity(operands.len()); for operand in operands { match operand.evaluate() { @@ -217,9 +213,9 @@ fn maybe_dump_ast(result: &Result, String>) { if let Ok(debug_var) = env::var("EXPR_DEBUG_AST") { if debug_var == "1" { println!("EXPR_DEBUG_AST"); - match *result { - Ok(ref ast) => ast.debug_dump(), - Err(ref reason) => println!("\terr: {:?}", reason), + match result { + Ok(ast) => ast.debug_dump(), + Err(reason) => println!("\terr: {:?}", reason), } } } @@ -304,7 +300,7 @@ fn push_token_to_either_stack( out_stack: &mut TokenStack, op_stack: &mut TokenStack, ) -> Result<(), String> { - let result = match *token { + let result = match token { Token::Value { .. } => { out_stack.push((token_idx, token.clone())); Ok(()) @@ -420,24 +416,14 @@ fn move_till_match_paren( } } -fn checked_binop Option, T>(cb: F, op: &str) -> Result { - match cb() { - Some(v) => Ok(v), - None => Err(format!("{}: Numerical result out of range", op)), - } -} - fn infix_operator_two_ints(f: F, values: &[String]) -> Result where - F: Fn(i64, i64) -> Result, + F: Fn(BigInt, BigInt) -> Result, { assert!(values.len() == 2); - if let Ok(left) = values[0].parse::() { - if let Ok(right) = values[1].parse::() { - return match f(left, right) { - Ok(result) => Ok(result.to_string()), - Err(reason) => Err(reason), - }; + if let Ok(left) = values[0].parse::() { + if let Ok(right) = values[1].parse::() { + return f(left, right).map(|big_int| big_int.to_string()); } } Err("Expected an integer operand".to_string()) @@ -449,13 +435,14 @@ fn infix_operator_two_ints_or_two_strings( values: &[String], ) -> Result where - FI: Fn(i64, i64) -> Result, + FI: Fn(BigInt, BigInt) -> Result, FS: Fn(&String, &String) -> Result, { assert!(values.len() == 2); - if let (Some(a_int), Some(b_int)) = - (values[0].parse::().ok(), values[1].parse::().ok()) - { + if let (Some(a_int), Some(b_int)) = ( + values[0].parse::().ok(), + values[1].parse::().ok(), + ) { match fi(a_int, b_int) { Ok(result) => Ok(result.to_string()), Err(reason) => Err(reason), @@ -541,7 +528,7 @@ fn prefix_operator_substr(values: &[String]) -> String { subj.chars().skip(idx).take(len).collect() } -fn bool_as_int(b: bool) -> i64 { +fn bool_as_int(b: bool) -> u8 { if b { 1 } else { @@ -559,8 +546,8 @@ fn value_as_bool(s: &str) -> bool { if s.is_empty() { return false; } - match s.parse::() { - Ok(n) => n != 0, + match s.parse::() { + Ok(n) => n.is_one(), Err(_) => true, } } diff --git a/src/uu/expr/src/tokens.rs b/src/uu/expr/src/tokens.rs index b65b0d482..6f2795588 100644 --- a/src/uu/expr/src/tokens.rs +++ b/src/uu/expr/src/tokens.rs @@ -18,6 +18,8 @@ // spell-checker:ignore (ToDO) paren +use num_bigint::BigInt; + #[derive(Debug, Clone)] pub enum Token { Value { @@ -51,24 +53,19 @@ impl Token { } fn is_infix_plus(&self) -> bool { - match *self { - Token::InfixOp { ref value, .. } => value == "+", + match self { + Token::InfixOp { value, .. } => value == "+", _ => false, } } fn is_a_number(&self) -> bool { - match *self { - Token::Value { ref value, .. } => value.parse::().is_ok(), + match self { + Token::Value { value, .. } => value.parse::().is_ok(), _ => false, } } fn is_a_close_paren(&self) -> bool { - #[allow(clippy::match_like_matches_macro)] - // `matches!(...)` macro not stabilized until rust v1.42 - match *self { - Token::ParClose => true, - _ => false, - } + matches!(*self, Token::ParClose) } } @@ -147,7 +144,7 @@ fn push_token_if_not_escaped(acc: &mut Vec<(usize, Token)>, tok_idx: usize, toke // Smells heuristics... :( let prev_is_plus = match acc.last() { None => false, - Some(ref t) => t.1.is_infix_plus(), + Some(t) => t.1.is_infix_plus(), }; let should_use_as_escaped = if prev_is_plus && acc.len() >= 2 { let pre_prev = &acc[acc.len() - 2]; diff --git a/src/uu/factor/Cargo.toml b/src/uu/factor/Cargo.toml index eb34519f1..eb977760f 100644 --- a/src/uu/factor/Cargo.toml +++ b/src/uu/factor/Cargo.toml @@ -21,6 +21,7 @@ rand = { version = "0.7", features = ["small_rng"] } smallvec = { version = "0.6.14, < 1.0" } uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore" } uucore_procs = { version = ">=0.0.5", package = "uucore_procs", path = "../../uucore_procs" } +clap = "2.33" [dev-dependencies] paste = "0.1.18" diff --git a/src/uu/factor/sieve.rs b/src/uu/factor/sieve.rs index 41893699b..492c8159f 100644 --- a/src/uu/factor/sieve.rs +++ b/src/uu/factor/sieve.rs @@ -30,7 +30,7 @@ impl Iterator for Sieve { #[inline] fn next(&mut self) -> Option { - while let Some(n) = self.inner.next() { + for n in &mut self.inner { let mut prime = true; while let Some((next, inc)) = self.filts.peek() { // need to keep checking the min element of the heap diff --git a/src/uu/factor/src/cli.rs b/src/uu/factor/src/cli.rs index ee4c8a4c4..69a368479 100644 --- a/src/uu/factor/src/cli.rs +++ b/src/uu/factor/src/cli.rs @@ -13,18 +13,21 @@ use std::error::Error; use std::io::{self, stdin, stdout, BufRead, Write}; mod factor; +use clap::{App, Arg}; pub use factor::*; -use uucore::InvalidEncodingHandling; mod miller_rabin; pub mod numeric; mod rho; pub mod table; -static SYNTAX: &str = "[OPTION] [NUMBER]..."; -static SUMMARY: &str = "Print the prime factors of the given number(s). - If none are specified, read from standard input."; -static LONG_HELP: &str = ""; +static VERSION: &str = env!("CARGO_PKG_VERSION"); +static SUMMARY: &str = "Print the prime factors of the given NUMBER(s). +If none are specified, read from standard input."; + +mod options { + pub static NUMBER: &str = "NUMBER"; +} fn print_factors_str(num_str: &str, w: &mut impl io::Write) -> Result<(), Box> { num_str @@ -34,14 +37,21 @@ fn print_factors_str(num_str: &str, w: &mut impl io::Write) -> Result<(), Box i32 { - let matches = app!(SYNTAX, SUMMARY, LONG_HELP).parse( - args.collect_str(InvalidEncodingHandling::Ignore) - .accept_any(), - ); + let matches = App::new(executable!()) + .version(VERSION) + .about(SUMMARY) + .arg(Arg::with_name(options::NUMBER).multiple(true)) + .get_matches_from(args); let stdout = stdout(); let mut w = io::BufWriter::new(stdout.lock()); - if matches.free.is_empty() { + if let Some(values) = matches.values_of(options::NUMBER) { + for number in values { + if let Err(e) = print_factors_str(number, &mut w) { + show_warning!("{}: {}", number, e); + } + } + } else { let stdin = stdin(); for line in stdin.lock().lines() { @@ -51,12 +61,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } } } - } else { - for number in &matches.free { - if let Err(e) = print_factors_str(number, &mut w) { - show_warning!("{}: {}", number, e); - } - } } if let Err(e) = w.flush() { diff --git a/src/uu/fmt/src/parasplit.rs b/src/uu/fmt/src/parasplit.rs index 950b3f66d..71b5f62ec 100644 --- a/src/uu/fmt/src/parasplit.rs +++ b/src/uu/fmt/src/parasplit.rs @@ -264,12 +264,9 @@ impl<'a> ParagraphStream<'a> { return false; } - #[allow(clippy::match_like_matches_macro)] - // `matches!(...)` macro not stabilized until rust v1.42 - l_slice[..colon_posn].chars().all(|x| match x as usize { - y if !(33..=126).contains(&y) => false, - _ => true, - }) + l_slice[..colon_posn] + .chars() + .all(|x| !matches!(x as usize, y if !(33..=126).contains(&y))) } } } @@ -541,12 +538,7 @@ impl<'a> WordSplit<'a> { } fn is_punctuation(c: char) -> bool { - #[allow(clippy::match_like_matches_macro)] - // `matches!(...)` macro not stabilized until rust v1.42 - match c { - '!' | '.' | '?' => true, - _ => false, - } + matches!(c, '!' | '.' | '?') } } diff --git a/src/uu/hashsum/src/hashsum.rs b/src/uu/hashsum/src/hashsum.rs index 2e31ddd25..b1ba3c217 100644 --- a/src/uu/hashsum/src/hashsum.rs +++ b/src/uu/hashsum/src/hashsum.rs @@ -51,14 +51,23 @@ struct Options { } fn is_custom_binary(program: &str) -> bool { - #[allow(clippy::match_like_matches_macro)] - // `matches!(...)` macro not stabilized until rust v1.42 - match program { - "md5sum" | "sha1sum" | "sha224sum" | "sha256sum" | "sha384sum" | "sha512sum" - | "sha3sum" | "sha3-224sum" | "sha3-256sum" | "sha3-384sum" | "sha3-512sum" - | "shake128sum" | "shake256sum" | "b2sum" => true, - _ => false, - } + matches!( + program, + "md5sum" + | "sha1sum" + | "sha224sum" + | "sha256sum" + | "sha384sum" + | "sha512sum" + | "sha3sum" + | "sha3-224sum" + | "sha3-256sum" + | "sha3-384sum" + | "sha3-512sum" + | "shake128sum" + | "shake256sum" + | "b2sum" + ) } #[allow(clippy::cognitive_complexity)] diff --git a/src/uu/head/src/parse.rs b/src/uu/head/src/parse.rs index 470d821e0..0cf20be42 100644 --- a/src/uu/head/src/parse.rs +++ b/src/uu/head/src/parse.rs @@ -14,7 +14,7 @@ pub fn parse_obsolete(src: &str) -> Option let mut num_end = 0usize; let mut has_num = false; let mut last_char = 0 as char; - while let Some((n, c)) = chars.next() { + for (n, c) in &mut chars { if c.is_numeric() { has_num = true; num_end = n; @@ -109,7 +109,7 @@ pub fn parse_num(src: &str) -> Result<(usize, bool), ParseError> { let mut num_end = 0usize; let mut last_char = 0 as char; let mut num_count = 0usize; - while let Some((n, c)) = chars.next() { + for (n, c) in &mut chars { if c.is_numeric() { num_end = n; num_count += 1; diff --git a/src/uu/install/src/install.rs b/src/uu/install/src/install.rs index bb51a7606..7a4ad1fd1 100644 --- a/src/uu/install/src/install.rs +++ b/src/uu/install/src/install.rs @@ -520,6 +520,7 @@ fn copy_file_to_file(file: &Path, target: &Path, b: &Behavior) -> i32 { /// /// If the copy system call fails, we print a verbose error and return an empty error value. /// +#[allow(clippy::cognitive_complexity)] fn copy(from: &Path, to: &Path, b: &Behavior) -> Result<(), ()> { if b.compare && !need_copy(from, to, b) { return Ok(()); diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index d467d431a..5846cb0aa 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -218,6 +218,7 @@ struct LongFormat { } impl Config { + #[allow(clippy::cognitive_complexity)] fn from(options: clap::ArgMatches) -> Config { let (mut format, opt) = if let Some(format_) = options.value_of(options::FORMAT) { ( @@ -1222,7 +1223,7 @@ fn list(locs: Vec, config: Config) -> i32 { sort_entries(&mut dirs, &config); for dir in dirs { - if locs.len() > 1 { + if locs.len() > 1 || config.recursive { let _ = writeln!(out, "\n{}:", dir.p_buf.display()); } enter_directory(&dir, &config, &mut out); @@ -1614,7 +1615,7 @@ fn display_date(metadata: &Metadata, config: &Config) -> String { Some(time) => { //Date is recent if from past 6 months //According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average. - let recent = time + chrono::Duration::seconds(31556952 / 2) > chrono::Local::now(); + let recent = time + chrono::Duration::seconds(31_556_952 / 2) > chrono::Local::now(); match config.time_style { TimeStyle::FullIso => time.format("%Y-%m-%d %H:%M:%S.%f %z"), @@ -1696,7 +1697,6 @@ fn file_is_executable(md: &Metadata) -> bool { md.mode() & ((S_IXUSR | S_IXGRP | S_IXOTH) as u32) != 0 } -#[allow(clippy::clippy::collapsible_else_if)] fn classify_file(path: &PathData) -> Option { let file_type = path.file_type()?; diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index 112c2fb94..f6c244bf2 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -15,14 +15,11 @@ use clap::{App, Arg}; use std::env; use std::iter; -use std::mem::forget; use std::path::{is_separator, PathBuf}; use rand::Rng; use tempfile::Builder; -mod tempdir; - static ABOUT: &str = "create a temporary file or directory."; static VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -213,50 +210,48 @@ pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) -> 0 } -fn exec( - tmpdir: PathBuf, - prefix: &str, - rand: usize, - suffix: &str, - make_dir: bool, - quiet: bool, -) -> i32 { - if make_dir { - match tempdir::new_in(&tmpdir, prefix, rand, suffix) { - Ok(ref f) => { - println!("{}", f); - return 0; - } - Err(e) => { - if !quiet { - show_error!("{}: {}", e, tmpdir.display()); +fn exec(dir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool, quiet: bool) -> i32 { + let res = if make_dir { + let tmpdir = Builder::new() + .prefix(prefix) + .rand_bytes(rand) + .suffix(suffix) + .tempdir_in(&dir); + + // `into_path` consumes the TempDir without removing it + tmpdir.map(|d| d.into_path().to_string_lossy().to_string()) + } else { + let tmpfile = Builder::new() + .prefix(prefix) + .rand_bytes(rand) + .suffix(suffix) + .tempfile_in(&dir); + + match tmpfile { + Ok(f) => { + // `keep` ensures that the file is not deleted + match f.keep() { + Ok((_, p)) => Ok(p.to_string_lossy().to_string()), + Err(e) => { + show_error!("'{}': {}", dir.display(), e); + return 1; + } } - return 1; } - } - } - let tmpfile = Builder::new() - .prefix(prefix) - .rand_bytes(rand) - .suffix(suffix) - .tempfile_in(tmpdir); - let tmpfile = match tmpfile { - Ok(f) => f, - Err(e) => { - if !quiet { - show_error!("failed to create tempfile: {}", e); - } - return 1; + Err(x) => Err(x), } }; - let tmpname = tmpfile.path().to_string_lossy().to_string(); - - println!("{}", tmpname); - - // CAUTION: Not to call `drop` of tmpfile, which removes the tempfile, - // I call a dangerous function `forget`. - forget(tmpfile); - - 0 + match res { + Ok(ref f) => { + println!("{}", f); + 0 + } + Err(e) => { + if !quiet { + show_error!("{}: {}", e, dir.display()); + } + 1 + } + } } diff --git a/src/uu/mktemp/src/tempdir.rs b/src/uu/mktemp/src/tempdir.rs deleted file mode 100644 index 1b6c9d7b3..000000000 --- a/src/uu/mktemp/src/tempdir.rs +++ /dev/null @@ -1,51 +0,0 @@ -// spell-checker:ignore (ToDO) tempdir tmpdir - -// Mainly taken from crate `tempdir` - -use rand::distributions::Alphanumeric; -use rand::{thread_rng, Rng}; - -use std::io::Result as IOResult; -use std::io::{Error, ErrorKind}; -use std::path::Path; - -// How many times should we (re)try finding an unused random name? It should be -// enough that an attacker will run out of luck before we run out of patience. -const NUM_RETRIES: u32 = 1 << 31; - -#[cfg(any(unix, target_os = "redox"))] -fn create_dir>(path: P) -> IOResult<()> { - use std::fs::DirBuilder; - use std::os::unix::fs::DirBuilderExt; - - DirBuilder::new().mode(0o700).create(path) -} - -#[cfg(windows)] -fn create_dir>(path: P) -> IOResult<()> { - ::std::fs::create_dir(path) -} - -pub fn new_in>( - tmpdir: P, - prefix: &str, - rand: usize, - suffix: &str, -) -> IOResult { - let mut rng = thread_rng(); - for _ in 0..NUM_RETRIES { - let rand_chars: String = rng.sample_iter(&Alphanumeric).take(rand).collect(); - let leaf = format!("{}{}{}", prefix, rand_chars, suffix); - let path = tmpdir.as_ref().join(&leaf); - match create_dir(&path) { - Ok(_) => return Ok(path.to_string_lossy().into_owned()), - Err(ref e) if e.kind() == ErrorKind::AlreadyExists => {} - Err(e) => return Err(e), - } - } - - Err(Error::new( - ErrorKind::AlreadyExists, - "too many temporary directories already exist", - )) -} diff --git a/src/uu/more/Cargo.toml b/src/uu/more/Cargo.toml index 1f4bfed68..9b1a3d7b6 100644 --- a/src/uu/more/Cargo.toml +++ b/src/uu/more/Cargo.toml @@ -18,6 +18,8 @@ path = "src/more.rs" clap = "2.33" uucore = { version = ">=0.0.7", package = "uucore", path = "../../uucore" } uucore_procs = { version = ">=0.0.5", package = "uucore_procs", path = "../../uucore_procs" } +crossterm = ">=0.19" +atty = "0.2.14" [target.'cfg(target_os = "redox")'.dependencies] redox_termios = "0.1" diff --git a/src/uu/more/src/more.rs b/src/uu/more/src/more.rs index eabdbee85..c4a13aa1b 100644 --- a/src/uu/more/src/more.rs +++ b/src/uu/more/src/more.rs @@ -10,150 +10,395 @@ #[macro_use] extern crate uucore; -use std::fs::File; -use std::io::{stdin, stdout, BufRead, BufReader, Read, Write}; +use std::{ + convert::TryInto, + fs::File, + io::{stdin, stdout, BufReader, Read, Stdout, Write}, + path::Path, + time::Duration, +}; #[cfg(all(unix, not(target_os = "fuchsia")))] extern crate nix; -#[cfg(all(unix, not(target_os = "fuchsia")))] -use nix::sys::termios::{self, LocalFlags, SetArg}; -use uucore::InvalidEncodingHandling; -#[cfg(target_os = "redox")] -extern crate redox_termios; -#[cfg(target_os = "redox")] -extern crate syscall; +use clap::{App, Arg}; +use crossterm::{ + event::{self, Event, KeyCode, KeyEvent, KeyModifiers}, + execute, queue, + style::Attribute, + terminal, +}; -use clap::{App, Arg, ArgMatches}; - -static VERSION: &str = env!("CARGO_PKG_VERSION"); -static ABOUT: &str = "A file perusal filter for CRT viewing."; - -mod options { - pub const FILE: &str = "file"; +pub mod options { + pub const SILENT: &str = "silent"; + pub const LOGICAL: &str = "logical"; + pub const NO_PAUSE: &str = "no-pause"; + pub const PRINT_OVER: &str = "print-over"; + pub const CLEAN_PRINT: &str = "clean-print"; + pub const SQUEEZE: &str = "squeeze"; + pub const PLAIN: &str = "plain"; + pub const LINES: &str = "lines"; + pub const NUMBER: &str = "number"; + pub const PATTERN: &str = "pattern"; + pub const FROM_LINE: &str = "from-line"; + pub const FILES: &str = "files"; } -fn get_usage() -> String { - format!("{} [options] ...", executable!()) -} +const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n"; pub fn uumain(args: impl uucore::Args) -> i32 { - let usage = get_usage(); - let args = args - .collect_str(InvalidEncodingHandling::ConvertLossy) - .accept_any(); - let matches = App::new(executable!()) - .version(VERSION) - .usage(usage.as_str()) - .about(ABOUT) + .about("A file perusal filter for CRT viewing.") + .version(env!("CARGO_PKG_VERSION")) + // The commented arguments below are unimplemented: + /* .arg( - Arg::with_name(options::FILE) - .number_of_values(1) - .multiple(true), + Arg::with_name(options::SILENT) + .short("d") + .long(options::SILENT) + .help("Display help instead of ringing bell"), + ) + .arg( + Arg::with_name(options::LOGICAL) + .short("f") + .long(options::LOGICAL) + .help("Count logical rather than screen lines"), + ) + .arg( + Arg::with_name(options::NO_PAUSE) + .short("l") + .long(options::NO_PAUSE) + .help("Suppress pause after form feed"), + ) + .arg( + Arg::with_name(options::PRINT_OVER) + .short("c") + .long(options::PRINT_OVER) + .help("Do not scroll, display text and clean line ends"), + ) + .arg( + Arg::with_name(options::CLEAN_PRINT) + .short("p") + .long(options::CLEAN_PRINT) + .help("Do not scroll, clean screen and display text"), + ) + .arg( + Arg::with_name(options::SQUEEZE) + .short("s") + .long(options::SQUEEZE) + .help("Squeeze multiple blank lines into one"), + ) + .arg( + Arg::with_name(options::PLAIN) + .short("u") + .long(options::PLAIN) + .help("Suppress underlining and bold"), + ) + .arg( + Arg::with_name(options::LINES) + .short("n") + .long(options::LINES) + .value_name("number") + .takes_value(true) + .help("The number of lines per screenful"), + ) + .arg( + Arg::with_name(options::NUMBER) + .allow_hyphen_values(true) + .long(options::NUMBER) + .required(false) + .takes_value(true) + .help("Same as --lines"), + ) + .arg( + Arg::with_name(options::FROM_LINE) + .short("F") + .allow_hyphen_values(true) + .required(false) + .takes_value(true) + .value_name("number") + .help("Display file beginning from line number"), + ) + .arg( + Arg::with_name(options::PATTERN) + .short("P") + .allow_hyphen_values(true) + .required(false) + .takes_value(true) + .help("Display file beginning from pattern match"), + ) + */ + .arg( + Arg::with_name(options::FILES) + .required(false) + .multiple(true) + .help("Path to the files to be read"), ) .get_matches_from(args); - - // FixME: fail without panic for now; but `more` should work with no arguments (ie, for piped input) - if let None | Some("-") = matches.value_of(options::FILE) { - show_usage_error!("Reading from stdin isn't supported yet."); - return 1; - } - - if let Some(x) = matches.value_of(options::FILE) { - let path = std::path::Path::new(x); - if path.is_dir() { - show_usage_error!("'{}' is a directory.", x); - return 1; + + let mut buff = String::new(); + if let Some(filenames) = matches.values_of(options::FILES) { + let mut stdout = setup_term(); + let length = filenames.len(); + for (idx, fname) in filenames.enumerate() { + let fname = Path::new(fname); + if fname.is_dir() { + terminal::disable_raw_mode().unwrap(); + show_usage_error!("'{}' is a directory.", fname.display()); + return 1; + } + if !fname.exists() { + terminal::disable_raw_mode().unwrap(); + show_error!( + "cannot open {}: No such file or directory", + fname.display() + ); + return 1; + } + if length > 1 { + buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", fname.to_str().unwrap())); + } + let mut reader = BufReader::new(File::open(fname).unwrap()); + reader.read_to_string(&mut buff).unwrap(); + let is_last = idx + 1 == length; + more(&buff, &mut stdout, is_last); + buff.clear(); } + reset_term(&mut stdout); + } else if atty::isnt(atty::Stream::Stdin) { + stdin().read_to_string(&mut buff).unwrap(); + let mut stdout = setup_term(); + more(&buff, &mut stdout, true); + reset_term(&mut stdout); + } else { + show_usage_error!("bad usage"); } - - more(matches); - 0 } -#[cfg(all(unix, not(target_os = "fuchsia")))] -fn setup_term() -> termios::Termios { - let mut term = termios::tcgetattr(0).unwrap(); - // Unset canonical mode, so we get characters immediately - term.local_flags.remove(LocalFlags::ICANON); - // Disable local echo - term.local_flags.remove(LocalFlags::ECHO); - termios::tcsetattr(0, SetArg::TCSADRAIN, &term).unwrap(); - term +#[cfg(not(target_os = "fuchsia"))] +fn setup_term() -> std::io::Stdout { + let stdout = stdout(); + terminal::enable_raw_mode().unwrap(); + stdout } -#[cfg(any(windows, target_os = "fuchsia"))] +#[cfg(target_os = "fuchsia")] #[inline(always)] fn setup_term() -> usize { 0 } -#[cfg(target_os = "redox")] -fn setup_term() -> redox_termios::Termios { - let mut term = redox_termios::Termios::default(); - let fd = syscall::dup(0, b"termios").unwrap(); - syscall::read(fd, &mut term).unwrap(); - term.local_flags &= !redox_termios::ICANON; - term.local_flags &= !redox_termios::ECHO; - syscall::write(fd, &term).unwrap(); - let _ = syscall::close(fd); - term +#[cfg(not(target_os = "fuchsia"))] +fn reset_term(stdout: &mut std::io::Stdout) { + terminal::disable_raw_mode().unwrap(); + // Clear the prompt + queue!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap(); + // Move cursor to the beginning without printing new line + print!("\r"); + stdout.flush().unwrap(); } -#[cfg(all(unix, not(target_os = "fuchsia")))] -fn reset_term(term: &mut termios::Termios) { - term.local_flags.insert(LocalFlags::ICANON); - term.local_flags.insert(LocalFlags::ECHO); - termios::tcsetattr(0, SetArg::TCSADRAIN, &term).unwrap(); -} - -#[cfg(any(windows, target_os = "fuchsia"))] +#[cfg(target_os = "fuchsia")] #[inline(always)] fn reset_term(_: &mut usize) {} -#[cfg(any(target_os = "redox"))] -fn reset_term(term: &mut redox_termios::Termios) { - let fd = syscall::dup(0, b"termios").unwrap(); - syscall::read(fd, term).unwrap(); - term.local_flags |= redox_termios::ICANON; - term.local_flags |= redox_termios::ECHO; - syscall::write(fd, &term).unwrap(); - let _ = syscall::close(fd); -} +fn more(buff: &str, mut stdout: &mut Stdout, is_last: bool) { + let (cols, rows) = terminal::size().unwrap(); + let lines = break_buff(buff, usize::from(cols)); + let line_count: u16 = lines.len().try_into().unwrap(); -fn more(matches: ArgMatches) { - let mut f: Box = match matches.value_of(options::FILE) { - None | Some("-") => Box::new(BufReader::new(stdin())), - Some(filename) => Box::new(BufReader::new(File::open(filename).unwrap())), - }; - let mut buffer = [0; 1024]; + let mut upper_mark = 0; + let mut lines_left = line_count.saturating_sub(upper_mark + rows); - let mut term = setup_term(); + draw( + &mut upper_mark, + rows, + &mut stdout, + lines.clone(), + line_count, + ); - let mut end = false; - while let Ok(sz) = f.read(&mut buffer) { - if sz == 0 { - break; - } - stdout().write_all(&buffer[0..sz]).unwrap(); - for byte in std::io::stdin().bytes() { - match byte.unwrap() { - b' ' => break, - b'q' | 27 => { - end = true; - break; - } - _ => (), - } - } - - if end { - break; + // Specifies whether we have reached the end of the file and should + // return on the next keypress. However, we immediately return when + // this is the last file. + let mut to_be_done = false; + if lines_left == 0 && is_last { + if is_last { + return; + } else { + to_be_done = true; } } + + loop { + if event::poll(Duration::from_millis(10)).unwrap() { + match event::read().unwrap() { + Event::Key(KeyEvent { + code: KeyCode::Char('q'), + modifiers: KeyModifiers::NONE, + }) + | Event::Key(KeyEvent { + code: KeyCode::Char('c'), + modifiers: KeyModifiers::CONTROL, + }) => { + reset_term(&mut stdout); + std::process::exit(0); + } + Event::Key(KeyEvent { + code: KeyCode::Down, + modifiers: KeyModifiers::NONE, + }) + | Event::Key(KeyEvent { + code: KeyCode::Char(' '), + modifiers: KeyModifiers::NONE, + }) => { + upper_mark = upper_mark.saturating_add(rows.saturating_sub(1)); + + } + Event::Key(KeyEvent { + code: KeyCode::Up, + modifiers: KeyModifiers::NONE, + }) => { + upper_mark = upper_mark.saturating_sub(rows.saturating_sub(1)); + } + _ => continue, + } + lines_left = line_count.saturating_sub(upper_mark + rows); + draw( + &mut upper_mark, + rows, + &mut stdout, + lines.clone(), + line_count, + ); - reset_term(&mut term); - println!(); + if lines_left == 0 { + if to_be_done || is_last { + return + } + to_be_done = true; + } + } + } +} + +fn draw( + upper_mark: &mut u16, + rows: u16, + mut stdout: &mut std::io::Stdout, + lines: Vec, + lc: u16, +) { + execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap(); + let (up_mark, lower_mark) = calc_range(*upper_mark, rows, lc); + // Reduce the row by 1 for the prompt + let displayed_lines = lines + .iter() + .skip(up_mark.into()) + .take(usize::from(rows.saturating_sub(1))); + + for line in displayed_lines { + stdout + .write_all(format!("\r{}\n", line).as_bytes()) + .unwrap(); + } + make_prompt_and_flush(&mut stdout, lower_mark, lc); + *upper_mark = up_mark; +} + +// Break the lines on the cols of the terminal +fn break_buff(buff: &str, cols: usize) -> Vec { + let mut lines = Vec::new(); + + for l in buff.lines() { + lines.append(&mut break_line(l, cols)); + } + lines +} + +fn break_line(mut line: &str, cols: usize) -> Vec { + let breaks = (line.len() / cols).saturating_add(1); + let mut lines = Vec::with_capacity(breaks); + // TODO: Use unicode width instead of the length in bytes. + if line.len() < cols { + lines.push(line.to_string()); + return lines; + } + + for _ in 1..=breaks { + let (line1, line2) = line.split_at(cols); + lines.push(line1.to_string()); + if line2.len() < cols { + lines.push(line2.to_string()); + break; + } + line = line2; + } + lines +} + +// Calculate upper_mark based on certain parameters +fn calc_range(mut upper_mark: u16, rows: u16, line_count: u16) -> (u16, u16) { + let mut lower_mark = upper_mark.saturating_add(rows); + + if lower_mark >= line_count { + upper_mark = line_count.saturating_sub(rows); + lower_mark = line_count; + } else { + lower_mark = lower_mark.saturating_sub(1) + } + (upper_mark, lower_mark) +} + +// Make a prompt similar to original more +fn make_prompt_and_flush(stdout: &mut Stdout, lower_mark: u16, lc: u16) { + write!( + stdout, + "\r{}--More--({}%){}", + Attribute::Reverse, + ((lower_mark as f64 / lc as f64) * 100.0).round() as u16, + Attribute::Reset + ) + .unwrap(); + stdout.flush().unwrap(); +} + +#[cfg(test)] +mod tests { + use super::{break_line, calc_range}; + + // It is good to test the above functions + #[test] + fn test_calc_range() { + assert_eq!((0, 24), calc_range(0, 25, 100)); + assert_eq!((50, 74), calc_range(50, 25, 100)); + assert_eq!((75, 100), calc_range(85, 25, 100)); + } + #[test] + fn test_break_lines_long() { + let mut test_string = String::with_capacity(100); + for _ in 0..200 { + test_string.push('#'); + } + + let lines = break_line(&test_string, 80); + + assert_eq!( + (80, 80, 40), + (lines[0].len(), lines[1].len(), lines[2].len()) + ); + } + + #[test] + fn test_break_lines_short() { + let mut test_string = String::with_capacity(100); + for _ in 0..20 { + test_string.push('#'); + } + + let lines = break_line(&test_string, 80); + + assert_eq!(20, lines[0].len()); + } } diff --git a/src/uu/od/src/parse_formats.rs b/src/uu/od/src/parse_formats.rs index 8b32d648c..abf05ea18 100644 --- a/src/uu/od/src/parse_formats.rs +++ b/src/uu/od/src/parse_formats.rs @@ -85,12 +85,7 @@ fn od_format_type(type_char: FormatType, byte_size: u8) -> Option bool { - #[allow(clippy::match_like_matches_macro)] - // `matches!(...)` macro not stabilized until rust v1.42 - match ch { - 'A' | 'j' | 'N' | 'S' | 'w' => true, - _ => false, - } + matches!(ch, 'A' | 'j' | 'N' | 'S' | 'w') } /// Parses format flags from command line diff --git a/src/uu/od/src/parse_inputs.rs b/src/uu/od/src/parse_inputs.rs index 533f4f106..288c0870f 100644 --- a/src/uu/od/src/parse_inputs.rs +++ b/src/uu/od/src/parse_inputs.rs @@ -76,7 +76,7 @@ pub fn parse_inputs(matches: &dyn CommandLineOpts) -> Result) -> Result CommandLineInputs::FileAndOffset(("-".to_string(), n, None)), _ => CommandLineInputs::FileNames( - input_strings.iter().map(|s| s.to_string()).collect(), + input_strings.iter().map(|&s| s.to_string()).collect(), ), }) } @@ -179,7 +179,7 @@ mod tests { impl<'a> MockOptions<'a> { fn new(inputs: Vec<&'a str>, option_names: Vec<&'a str>) -> MockOptions<'a> { MockOptions { - inputs: inputs.iter().map(|s| s.to_string()).collect::>(), + inputs: inputs.iter().map(|&s| s.to_string()).collect::>(), option_names, } } diff --git a/src/uu/printenv/src/printenv.rs b/src/uu/printenv/src/printenv.rs index 34571ddad..25cb58185 100644 --- a/src/uu/printenv/src/printenv.rs +++ b/src/uu/printenv/src/printenv.rs @@ -50,10 +50,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .map(|v| v.map(ToString::to_string).collect()) .unwrap_or_default(); - let mut separator = "\n"; - if matches.is_present(OPT_NULL) { - separator = "\x00"; - } + let separator = if matches.is_present(OPT_NULL) { + "\x00" + } else { + "\n" + }; if variables.is_empty() { for (env_var, value) in env::vars() { diff --git a/src/uu/ptx/src/ptx.rs b/src/uu/ptx/src/ptx.rs index d2aa619b4..a17f7c810 100644 --- a/src/uu/ptx/src/ptx.rs +++ b/src/uu/ptx/src/ptx.rs @@ -108,10 +108,13 @@ impl WordFilter { // Ignore empty string regex from cmd-line-args let arg_reg: Option = if matches.is_present(options::WORD_REGEXP) { match matches.value_of(options::WORD_REGEXP) { - Some(v) => match v.is_empty() { - true => None, - false => Some(v.to_string()), - }, + Some(v) => { + if v.is_empty() { + None + } else { + Some(v.to_string()) + } + } None => None, } } else { diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index 94626b4e7..8010988bb 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -386,13 +386,8 @@ fn prompt(msg: &str) -> bool { let stdin = stdin(); let mut stdin = stdin.lock(); - #[allow(clippy::match_like_matches_macro)] - // `matches!(...)` macro not stabilized until rust v1.42 match stdin.read_until(b'\n', &mut buf) { - Ok(x) if x > 0 => match buf[0] { - b'y' | b'Y' => true, - _ => false, - }, + Ok(x) if x > 0 => matches!(buf[0], b'y' | b'Y'), _ => false, } } diff --git a/src/uu/rmdir/src/rmdir.rs b/src/uu/rmdir/src/rmdir.rs index 7a7e8fc9b..bebb2844b 100644 --- a/src/uu/rmdir/src/rmdir.rs +++ b/src/uu/rmdir/src/rmdir.rs @@ -20,6 +20,11 @@ static OPT_VERBOSE: &str = "verbose"; static ARG_DIRS: &str = "dirs"; +#[cfg(unix)] +static ENOTDIR: i32 = 20; +#[cfg(windows)] +static ENOTDIR: i32 = 267; + fn get_usage() -> String { format!("{0} [OPTION]... DIRECTORY...", executable!()) } @@ -105,6 +110,10 @@ fn remove(dirs: Vec, ignore: bool, parents: bool, verbose: bool) -> Resu fn remove_dir(path: &Path, ignore: bool, verbose: bool) -> Result<(), i32> { let mut read_dir = match fs::read_dir(path) { Ok(m) => m, + Err(e) if e.raw_os_error() == Some(ENOTDIR) => { + show_error!("failed to remove '{}': Not a directory", path.display()); + return Err(1); + } Err(e) => { show_error!("reading directory '{}': {}", path.display(), e); return Err(1); diff --git a/src/uu/sort/BENCHMARKING.md b/src/uu/sort/BENCHMARKING.md index fd728c41d..560d6b438 100644 --- a/src/uu/sort/BENCHMARKING.md +++ b/src/uu/sort/BENCHMARKING.md @@ -2,7 +2,7 @@ Most of the time when sorting is spent comparing lines. The comparison functions however differ based on which arguments are passed to `sort`, therefore it is important to always benchmark multiple scenarios. -This is an overwiew over what was benchmarked, and if you make changes to `sort`, you are encouraged to check +This is an overview over what was benchmarked, and if you make changes to `sort`, you are encouraged to check how performance was affected for the workloads listed below. Feel free to add other workloads to the list that we should improve / make sure not to regress. diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index 724744dc4..f06610248 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -25,7 +25,7 @@ ouroboros = "0.9.3" rand = "0.7" rayon = "1.5" semver = "0.9.0" -tempdir = "0.3.7" +tempfile = "3" unicode-width = "0.1.8" uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/sort/src/check.rs b/src/uu/sort/src/check.rs index 01b5a25b5..d3b9d6669 100644 --- a/src/uu/sort/src/check.rs +++ b/src/uu/sort/src/check.rs @@ -26,7 +26,7 @@ use std::{ /// /// The code we should exit with. pub fn check(path: &str, settings: &GlobalSettings) -> i32 { - let file = open(path).expect("failed to open input file"); + let file = open(path); let (recycled_sender, recycled_receiver) = sync_channel(2); let (loaded_sender, loaded_receiver) = sync_channel(2); thread::spawn({ diff --git a/src/uu/sort/src/chunks.rs b/src/uu/sort/src/chunks.rs index 6ec759211..23567833b 100644 --- a/src/uu/sort/src/chunks.rs +++ b/src/uu/sort/src/chunks.rs @@ -73,6 +73,7 @@ impl Chunk { /// * `lines`: The recycled vector to fill with lines. Must be empty. /// * `settings`: The global settings. #[allow(clippy::too_many_arguments)] +#[allow(clippy::borrowed_box)] pub fn read( sender_option: &mut Option>, mut buffer: Vec, @@ -164,6 +165,7 @@ fn parse_lines<'a>( /// The remaining bytes must be copied to the start of the buffer for the next invocation, /// if another invocation is necessary, which is determined by the other return value. /// * Whether this function should be called again. +#[allow(clippy::borrowed_box)] fn read_to_buffer( file: &mut Box, next_files: &mut impl Iterator>, diff --git a/src/uu/sort/src/ext_sort.rs b/src/uu/sort/src/ext_sort.rs index a304bf7c0..9b1845efa 100644 --- a/src/uu/sort/src/ext_sort.rs +++ b/src/uu/sort/src/ext_sort.rs @@ -23,7 +23,7 @@ use std::{ use itertools::Itertools; -use tempdir::TempDir; +use tempfile::TempDir; use crate::{ chunks::{self, Chunk}, @@ -34,7 +34,12 @@ const MIN_BUFFER_SIZE: usize = 8_000; /// Sort files by using auxiliary files for storing intermediate chunks (if needed), and output the result. pub fn ext_sort(files: &mut impl Iterator>, settings: &GlobalSettings) { - let tmp_dir = crash_if_err!(1, TempDir::new_in(&settings.tmp_dir, "uutils_sort")); + let tmp_dir = crash_if_err!( + 1, + tempfile::Builder::new() + .prefix("uutils_sort") + .tempdir_in(&settings.tmp_dir) + ); let (sorted_sender, sorted_receiver) = std::sync::mpsc::sync_channel(1); let (recycled_sender, recycled_receiver) = std::sync::mpsc::sync_channel(1); thread::spawn({ diff --git a/src/uu/sort/src/merge.rs b/src/uu/sort/src/merge.rs index 48d48ad40..696353829 100644 --- a/src/uu/sort/src/merge.rs +++ b/src/uu/sort/src/merge.rs @@ -29,7 +29,7 @@ pub fn merge<'a>(files: &[impl AsRef], settings: &'a GlobalSettings) -> F let (request_sender, request_receiver) = channel(); let mut reader_files = Vec::with_capacity(files.len()); let mut loaded_receivers = Vec::with_capacity(files.len()); - for (file_number, file) in files.iter().filter_map(open).enumerate() { + for (file_number, file) in files.iter().map(open).enumerate() { let (sender, receiver) = sync_channel(2); loaded_receivers.push(receiver); reader_files.push(ReaderFile { diff --git a/src/uu/sort/src/numeric_str_cmp.rs b/src/uu/sort/src/numeric_str_cmp.rs index f8666b701..03806b0c8 100644 --- a/src/uu/sort/src/numeric_str_cmp.rs +++ b/src/uu/sort/src/numeric_str_cmp.rs @@ -68,10 +68,10 @@ impl NumInfo { } first_char = false; - if parse_settings - .thousands_separator - .map_or(false, |c| c == char) - { + if matches!( + parse_settings.thousands_separator, + Some(c) if c == char + ) { continue; } @@ -174,7 +174,11 @@ impl NumInfo { pub fn numeric_str_cmp((a, a_info): (&str, &NumInfo), (b, b_info): (&str, &NumInfo)) -> Ordering { // check for a difference in the sign if a_info.sign != b_info.sign { - return a_info.sign.cmp(&b_info.sign); + return if a.is_empty() && b.is_empty() { + Ordering::Equal + } else { + a_info.sign.cmp(&b_info.sign) + }; } // check for a difference in the exponent @@ -419,8 +423,8 @@ mod tests { #[test] fn minus_zero() { // This matches GNU sort behavior. - test_helper("-0", "0", Ordering::Less); - test_helper("-0x", "0", Ordering::Less); + test_helper("-0", "0", Ordering::Equal); + test_helper("-0x", "0", Ordering::Equal); } #[test] fn double_minus() { diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 6d79e80fb..a3b79e5d7 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -99,17 +99,16 @@ static OPT_TMP_DIR: &str = "temporary-directory"; static ARG_FILES: &str = "files"; static DECIMAL_PT: char = '.'; -static THOUSANDS_SEP: char = ','; -static NEGATIVE: char = '-'; -static POSITIVE: char = '+'; +const NEGATIVE: char = '-'; +const POSITIVE: char = '+'; // Choosing a higher buffer size does not result in performance improvements // (at least not on my machine). TODO: In the future, we should also take the amount of // available memory into consideration, instead of relying on this constant only. static DEFAULT_BUF_SIZE: usize = 1_000_000_000; // 1 GB -#[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Copy)] +#[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Copy, Debug)] enum SortMode { Numeric, HumanNumeric, @@ -119,6 +118,21 @@ enum SortMode { Random, Default, } + +impl SortMode { + fn get_short_name(&self) -> Option { + match self { + SortMode::Numeric => Some('n'), + SortMode::HumanNumeric => Some('h'), + SortMode::GeneralNumeric => Some('g'), + SortMode::Month => Some('M'), + SortMode::Version => Some('V'), + SortMode::Random => Some('R'), + SortMode::Default => None, + } + } +} + #[derive(Clone)] pub struct GlobalSettings { mode: SortMode, @@ -212,7 +226,7 @@ impl Default for GlobalSettings { } } } -#[derive(Clone)] +#[derive(Clone, PartialEq, Debug)] struct KeySettings { mode: SortMode, ignore_blanks: bool, @@ -222,6 +236,60 @@ struct KeySettings { reverse: bool, } +impl KeySettings { + /// Checks if the supplied combination of `mode`, `ignore_non_printing` and `dictionary_order` is allowed. + fn check_compatibility( + mode: SortMode, + ignore_non_printing: bool, + dictionary_order: bool, + ) -> Result<(), String> { + if matches!( + mode, + SortMode::Numeric | SortMode::HumanNumeric | SortMode::GeneralNumeric | SortMode::Month + ) { + if dictionary_order { + return Err(format!( + "options '-{}{}' are incompatible", + 'd', + mode.get_short_name().unwrap() + )); + } else if ignore_non_printing { + return Err(format!( + "options '-{}{}' are incompatible", + 'i', + mode.get_short_name().unwrap() + )); + } + } + Ok(()) + } + + fn set_sort_mode(&mut self, mode: SortMode) -> Result<(), String> { + if self.mode != SortMode::Default { + return Err(format!( + "options '-{}{}' are incompatible", + self.mode.get_short_name().unwrap(), + mode.get_short_name().unwrap() + )); + } + Self::check_compatibility(mode, self.ignore_non_printing, self.dictionary_order)?; + self.mode = mode; + Ok(()) + } + + fn set_dictionary_order(&mut self) -> Result<(), String> { + Self::check_compatibility(self.mode, self.ignore_non_printing, true)?; + self.dictionary_order = true; + Ok(()) + } + + fn set_ignore_non_printing(&mut self) -> Result<(), String> { + Self::check_compatibility(self.mode, true, self.dictionary_order)?; + self.ignore_non_printing = true; + Ok(()) + } +} + impl From<&GlobalSettings> for KeySettings { fn from(settings: &GlobalSettings) -> Self { Self { @@ -235,6 +303,12 @@ impl From<&GlobalSettings> for KeySettings { } } +impl Default for KeySettings { + fn default() -> Self { + Self::from(&GlobalSettings::default()) + } +} + #[derive(Clone, Debug)] enum NumCache { AsF64(GeneralF64ParseResult), @@ -297,10 +371,10 @@ impl<'a> Line<'a> { fn print(&self, writer: &mut impl Write, settings: &GlobalSettings) { if settings.zero_terminated && !settings.debug { crash_if_err!(1, writer.write_all(self.line.as_bytes())); - crash_if_err!(1, writer.write_all("\0".as_bytes())); + crash_if_err!(1, writer.write_all(b"\0")); } else if !settings.debug { crash_if_err!(1, writer.write_all(self.line.as_bytes())); - crash_if_err!(1, writer.write_all("\n".as_bytes())); + crash_if_err!(1, writer.write_all(b"\n")); } else { crash_if_err!(1, self.print_debug(settings, writer)); } @@ -330,8 +404,7 @@ impl<'a> Line<'a> { &self.line[selection.clone()], NumInfoParseSettings { accept_si_units: selector.settings.mode == SortMode::HumanNumeric, - thousands_separator: Some(THOUSANDS_SEP), - decimal_pt: Some(DECIMAL_PT), + ..Default::default() }, ); let initial_selection = selection.clone(); @@ -367,16 +440,24 @@ impl<'a> Line<'a> { SortMode::Month => { let initial_selection = &self.line[selection.clone()]; + let mut month_chars = initial_selection + .char_indices() + .skip_while(|(_, c)| c.is_whitespace()); + let month = if month_parse(initial_selection) == Month::Unknown { // We failed to parse a month, which is equivalent to matching nothing. - 0..0 + // Add the "no match for key" marker to the first non-whitespace character. + let first_non_whitespace = month_chars.next(); + first_non_whitespace.map_or( + initial_selection.len()..initial_selection.len(), + |(idx, _)| idx..idx, + ) } else { - // We parsed a month. Match the three first non-whitespace characters, which must be the month we parsed. - let mut chars = initial_selection - .char_indices() - .skip_while(|(_, c)| c.is_whitespace()); - chars.next().unwrap().0 - ..chars.nth(2).map_or(initial_selection.len(), |(idx, _)| idx) + // We parsed a month. Match the first three non-whitespace characters, which must be the month we parsed. + month_chars.next().unwrap().0 + ..month_chars + .nth(2) + .map_or(initial_selection.len(), |(idx, _)| idx) }; // Shorten selection to month. @@ -406,14 +487,18 @@ impl<'a> Line<'a> { } } } - if !(settings.mode == SortMode::Random - || settings.stable - || settings.unique - || !(settings.dictionary_order + if settings.mode != SortMode::Random + && !settings.stable + && !settings.unique + && (settings.dictionary_order || settings.ignore_blanks || settings.ignore_case || settings.ignore_non_printing - || settings.mode != SortMode::Default)) + || settings.mode != SortMode::Default + || settings + .selectors + .last() + .map_or(true, |selector| selector != &Default::default())) { // A last resort comparator is in use, underline the whole line. if self.line.is_empty() { @@ -477,7 +562,7 @@ fn tokenize_with_separator(line: &str, separator: char) -> Vec { tokens } -#[derive(Clone)] +#[derive(Clone, PartialEq, Debug)] struct KeyPosition { /// 1-indexed, 0 is invalid. field: usize, @@ -487,87 +572,45 @@ struct KeyPosition { } impl KeyPosition { - fn parse(key: &str, default_char_index: usize, settings: &mut KeySettings) -> Self { + fn new(key: &str, default_char_index: usize, ignore_blanks: bool) -> Result { let mut field_and_char = key.split('.'); - let mut field = field_and_char + + let field = field_and_char .next() - .unwrap_or_else(|| crash!(1, "invalid key `{}`", key)); - let mut char = field_and_char.next(); - - // If there is a char index, we expect options to appear after it. Otherwise we expect them after the field index. - let value_with_options = char.as_mut().unwrap_or(&mut field); - - let mut ignore_blanks = settings.ignore_blanks; - if let Some(options_start) = value_with_options.chars().position(char::is_alphabetic) { - for option in value_with_options[options_start..].chars() { - // valid options: MbdfghinRrV - match option { - 'M' => settings.mode = SortMode::Month, - 'b' => ignore_blanks = true, - 'd' => settings.dictionary_order = true, - 'f' => settings.ignore_case = true, - 'g' => settings.mode = SortMode::GeneralNumeric, - 'h' => settings.mode = SortMode::HumanNumeric, - 'i' => settings.ignore_non_printing = true, - 'n' => settings.mode = SortMode::Numeric, - 'R' => settings.mode = SortMode::Random, - 'r' => settings.reverse = true, - 'V' => settings.mode = SortMode::Version, - c => crash!(1, "invalid option for key: `{}`", c), - } - // All numeric sorts and month sort conflict with dictionary_order and ignore_non_printing. - // Instad of reporting an error, let them overwrite each other. - - // FIXME: This should only override if the overridden flag is a global flag. - // If conflicting flags are attached to the key, GNU sort crashes and we should probably too. - match option { - 'h' | 'n' | 'g' | 'M' => { - settings.dictionary_order = false; - settings.ignore_non_printing = false; - } - 'd' | 'i' => { - settings.mode = match settings.mode { - SortMode::Numeric - | SortMode::HumanNumeric - | SortMode::GeneralNumeric - | SortMode::Month => SortMode::Default, - // Only SortMode::Default and SortMode::Version work with dictionary_order and ignore_non_printing - m @ SortMode::Default - | m @ SortMode::Version - | m @ SortMode::Random => m, - } - } - _ => {} - } - } - // Strip away option characters from the original value so we can parse it later - *value_with_options = &value_with_options[..options_start]; - } + .ok_or_else(|| format!("invalid key `{}`", key))?; + let char = field_and_char.next(); let field = field .parse() - .unwrap_or_else(|e| crash!(1, "failed to parse field index for key `{}`: {}", key, e)); + .map_err(|e| format!("failed to parse field index `{}`: {}", field, e))?; if field == 0 { - crash!(1, "field index was 0"); + return Err("field index can not be 0".to_string()); } - let char = char.map_or(default_char_index, |char| { - char.parse().unwrap_or_else(|e| { - crash!( - 1, - "failed to parse character index for key `{}`: {}", - key, - e - ) - }) - }); - Self { + + let char = char.map_or(Ok(default_char_index), |char| { + char.parse() + .map_err(|e| format!("failed to parse character index `{}`: {}", char, e)) + })?; + + Ok(Self { field, char, ignore_blanks, + }) + } +} + +impl Default for KeyPosition { + fn default() -> Self { + KeyPosition { + field: 1, + char: 1, + ignore_blanks: false, } } } -#[derive(Clone)] + +#[derive(Clone, PartialEq, Debug)] struct FieldSelector { from: KeyPosition, to: Option, @@ -577,21 +620,120 @@ struct FieldSelector { is_default_selection: bool, } -impl FieldSelector { - fn new(from: KeyPosition, to: Option, settings: KeySettings) -> Self { +impl Default for FieldSelector { + fn default() -> Self { Self { - is_default_selection: from.field == 1 - && from.char == 1 - && to.is_none() - // TODO: Once our MinRustV is 1.42 or higher, change this to the matches! macro - && match settings.mode { - SortMode::Numeric | SortMode::GeneralNumeric | SortMode::HumanNumeric => false, - _ => true, - }, - needs_tokens: from.field != 1 || from.char == 0 || to.is_some(), - from, - to, - settings, + from: Default::default(), + to: None, + settings: Default::default(), + needs_tokens: false, + is_default_selection: true, + } + } +} + +impl FieldSelector { + /// Splits this position into the actual position and the attached options. + fn split_key_options(position: &str) -> (&str, &str) { + if let Some((options_start, _)) = position.char_indices().find(|(_, c)| c.is_alphabetic()) { + position.split_at(options_start) + } else { + (position, "") + } + } + + fn parse(key: &str, global_settings: &GlobalSettings) -> Self { + let mut from_to = key.split(','); + let (from, from_options) = Self::split_key_options(from_to.next().unwrap()); + let to = from_to.next().map(|to| Self::split_key_options(to)); + let options_are_empty = from_options.is_empty() && matches!(to, None | Some((_, ""))); + crash_if_err!( + 2, + if options_are_empty { + // Inherit the global settings if there are no options attached to this key. + (|| { + // This would be ideal for a try block, I think. In the meantime this closure allows + // to use the `?` operator here. + Self::new( + KeyPosition::new(from, 1, global_settings.ignore_blanks)?, + to.map(|(to, _)| KeyPosition::new(to, 0, global_settings.ignore_blanks)) + .transpose()?, + KeySettings::from(global_settings), + ) + })() + } else { + // Do not inherit from `global_settings`, as there are options attached to this key. + Self::parse_with_options((from, from_options), to) + } + .map_err(|e| format!("failed to parse key `{}`: {}", key, e)) + ) + } + + fn parse_with_options( + (from, from_options): (&str, &str), + to: Option<(&str, &str)>, + ) -> Result { + /// Applies `options` to `key_settings`, returning if the 'b'-flag (ignore blanks) was present. + fn parse_key_settings( + options: &str, + key_settings: &mut KeySettings, + ) -> Result { + let mut ignore_blanks = false; + for option in options.chars() { + match option { + 'M' => key_settings.set_sort_mode(SortMode::Month)?, + 'b' => ignore_blanks = true, + 'd' => key_settings.set_dictionary_order()?, + 'f' => key_settings.ignore_case = true, + 'g' => key_settings.set_sort_mode(SortMode::GeneralNumeric)?, + 'h' => key_settings.set_sort_mode(SortMode::HumanNumeric)?, + 'i' => key_settings.set_ignore_non_printing()?, + 'n' => key_settings.set_sort_mode(SortMode::Numeric)?, + 'R' => key_settings.set_sort_mode(SortMode::Random)?, + 'r' => key_settings.reverse = true, + 'V' => key_settings.set_sort_mode(SortMode::Version)?, + c => return Err(format!("invalid option: `{}`", c)), + } + } + Ok(ignore_blanks) + } + + let mut key_settings = KeySettings::default(); + let from = parse_key_settings(from_options, &mut key_settings) + .map(|ignore_blanks| KeyPosition::new(from, 1, ignore_blanks))??; + let to = if let Some((to, to_options)) = to { + Some( + parse_key_settings(to_options, &mut key_settings) + .map(|ignore_blanks| KeyPosition::new(to, 0, ignore_blanks))??, + ) + } else { + None + }; + Self::new(from, to, key_settings) + } + + fn new( + from: KeyPosition, + to: Option, + settings: KeySettings, + ) -> Result { + if from.char == 0 { + Err("invalid character index 0 for the start position of a field".to_string()) + } else { + Ok(Self { + is_default_selection: from.field == 1 + && from.char == 1 + && to.is_none() + && !matches!( + settings.mode, + SortMode::Numeric | SortMode::GeneralNumeric | SortMode::HumanNumeric + ) + && !from.ignore_blanks, + needs_tokens: from.field != 1 || from.char == 0 || to.is_some(), + from, + to, + settings, + }) } } @@ -607,8 +749,7 @@ impl FieldSelector { range, NumInfoParseSettings { accept_si_units: self.settings.mode == SortMode::HumanNumeric, - thousands_separator: Some(THOUSANDS_SEP), - decimal_pt: Some(DECIMAL_PT), + ..Default::default() }, ); // Shorten the range to what we need to pass to numeric_str_cmp later. @@ -650,7 +791,7 @@ impl FieldSelector { tokens: Option<&[Field]>, position: &KeyPosition, ) -> Resolution { - if tokens.map_or(false, |fields| fields.len() < position.field) { + if matches!(tokens, Some(tokens) if tokens.len() < position.field) { Resolution::TooHigh } else if position.char == 0 { let end = tokens.unwrap()[position.field - 1].end; @@ -667,22 +808,21 @@ impl FieldSelector { } else { tokens.unwrap()[position.field - 1].start }; + // strip blanks if needed + if position.ignore_blanks { + idx += line[idx..] + .char_indices() + .find(|(_, c)| !c.is_whitespace()) + .map_or(line[idx..].len(), |(idx, _)| idx); + } + // apply the character index idx += line[idx..] .char_indices() .nth(position.char - 1) - .map_or(line.len(), |(idx, _)| idx); + .map_or(line[idx..].len(), |(idx, _)| idx); if idx >= line.len() { Resolution::TooHigh } else { - if position.ignore_blanks { - if let Some((not_whitespace, _)) = - line[idx..].char_indices().find(|(_, c)| !c.is_whitespace()) - { - idx += not_whitespace; - } else { - return Resolution::TooHigh; - } - } Resolution::StartOfChar(idx) } } @@ -692,8 +832,9 @@ impl FieldSelector { Resolution::StartOfChar(from) => { let to = self.to.as_ref().map(|to| resolve_index(line, tokens, &to)); - match to { + let mut range = match to { Some(Resolution::StartOfChar(mut to)) => { + // We need to include the character at `to`. to += line[to..].chars().next().map_or(1, |c| c.len_utf8()); from..to } @@ -704,7 +845,11 @@ impl FieldSelector { // If `to` is before the start of the line, report no match. // This can happen if the line starts with a separator. Some(Resolution::TooLow) => 0..0, + }; + if range.start > range.end { + range.end = range.start; } + range } Resolution::TooLow | Resolution::EndOfChar(_) => { unreachable!("This should only happen if the field start index is 0, but that should already have caused an error.") @@ -892,7 +1037,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .long(OPT_SEPARATOR) .help("custom separator for -k") .takes_value(true)) - .arg(Arg::with_name(OPT_ZERO_TERMINATED) + .arg( + Arg::with_name(OPT_ZERO_TERMINATED) .short("z") .long(OPT_ZERO_TERMINATED) .help("line delimiter is NUL, not newline"), @@ -947,7 +1093,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let mut files = Vec::new(); for path in &files0_from { - let reader = open(path.as_str()).expect("Could not read from file specified."); + let reader = open(path.as_str()); let buf_reader = BufReader::new(reader); for line in buf_reader.split(b'\0').flatten() { files.push( @@ -1045,43 +1191,27 @@ pub fn uumain(args: impl uucore::Args) -> i32 { if matches.is_present(OPT_KEY) { for key in &matches.args[OPT_KEY].vals { - let key = key.to_string_lossy(); - let mut from_to = key.split(','); - let mut key_settings = KeySettings::from(&settings); - let from = KeyPosition::parse( - from_to - .next() - .unwrap_or_else(|| crash!(1, "invalid key `{}`", key)), - 1, - &mut key_settings, - ); - if from.char == 0 { - crash!( - 1, - "invalid character index 0 in `{}` for the start position of a field", - key - ) - } - let to = from_to - .next() - .map(|to| KeyPosition::parse(to, 0, &mut key_settings)); - let field_selector = FieldSelector::new(from, to, key_settings); - settings.selectors.push(field_selector); + settings + .selectors + .push(FieldSelector::parse(&key.to_string_lossy(), &settings)); } } - if !settings.stable || !matches.is_present(OPT_KEY) { + if !matches.is_present(OPT_KEY) { // add a default selector matching the whole line let key_settings = KeySettings::from(&settings); - settings.selectors.push(FieldSelector::new( - KeyPosition { - field: 1, - char: 1, - ignore_blanks: key_settings.ignore_blanks, - }, - None, - key_settings, - )); + settings.selectors.push( + FieldSelector::new( + KeyPosition { + field: 1, + char: 1, + ignore_blanks: key_settings.ignore_blanks, + }, + None, + key_settings, + ) + .unwrap(), + ); } exec(&files, &settings) @@ -1108,7 +1238,7 @@ fn exec(files: &[String], settings: &GlobalSettings) -> i32 { } return check::check(files.first().unwrap(), settings); } else { - let mut lines = files.iter().filter_map(open); + let mut lines = files.iter().map(open); ext_sort(&mut lines, &settings); } @@ -1203,6 +1333,8 @@ fn compare_by<'a>(a: &Line<'a>, b: &Line<'a>, global_settings: &GlobalSettings) fn get_leading_gen(input: &str) -> Range { let trimmed = input.trim_start(); let leading_whitespace_len = input.len() - trimmed.len(); + + // check for inf, -inf and nan for allowed_prefix in &["inf", "-inf", "nan"] { if trimmed.is_char_boundary(allowed_prefix.len()) && trimmed[..allowed_prefix.len()].eq_ignore_ascii_case(allowed_prefix) @@ -1211,11 +1343,11 @@ fn get_leading_gen(input: &str) -> Range { } } // Make this iter peekable to see if next char is numeric - let mut char_indices = trimmed.char_indices().peekable(); + let mut char_indices = itertools::peek_nth(trimmed.char_indices()); let first = char_indices.peek(); - if first.map_or(false, |&(_, c)| c == NEGATIVE || c == POSITIVE) { + if matches!(first, Some((_, NEGATIVE)) | Some((_, POSITIVE))) { char_indices.next(); } @@ -1225,16 +1357,29 @@ fn get_leading_gen(input: &str) -> Range { if c.is_ascii_digit() { continue; } - if c == DECIMAL_PT && !had_decimal_pt { + if c == DECIMAL_PT && !had_decimal_pt && !had_e_notation { had_decimal_pt = true; continue; } - let next_char_numeric = char_indices - .peek() - .map_or(false, |(_, c)| c.is_ascii_digit()); - if (c == 'e' || c == 'E') && !had_e_notation && next_char_numeric { - had_e_notation = true; - continue; + if (c == 'e' || c == 'E') && !had_e_notation { + // we can only consume the 'e' if what follow is either a digit, or a sign followed by a digit. + if let Some(&(_, next_char)) = char_indices.peek() { + if (next_char == '+' || next_char == '-') + && matches!( + char_indices.peek_nth(2), + Some((_, c)) if c.is_ascii_digit() + ) + { + // Consume the sign. The following digits will be consumed by the main loop. + char_indices.next(); + had_e_notation = true; + continue; + } + if next_char.is_ascii_digit() { + had_e_notation = true; + continue; + } + } } return leading_whitespace_len..(leading_whitespace_len + idx); } @@ -1390,18 +1535,17 @@ fn print_sorted<'a, T: Iterator>>(iter: T, settings: &Global } // from cat.rs -fn open(path: impl AsRef) -> Option> { +fn open(path: impl AsRef) -> Box { let path = path.as_ref(); if path == "-" { let stdin = stdin(); - return Some(Box::new(stdin) as Box); + return Box::new(stdin) as Box; } match File::open(Path::new(path)) { - Ok(f) => Some(Box::new(f) as Box), + Ok(f) => Box::new(f) as Box, Err(e) => { - show_error!("{0:?}: {1}", path, e.to_string()); - None + crash!(2, "cannot read: {0:?}: {1}", path, e); } } } @@ -1415,7 +1559,7 @@ mod tests { fn test_get_hash() { let a = "Ted".to_string(); - assert_eq!(2646829031758483623, get_hash(&a)); + assert_eq!(2_646_829_031_758_483_623, get_hash(&a)); } #[test] diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 726c9b8cd..39bd577cb 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -200,6 +200,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { split(&settings) } +#[allow(dead_code)] struct Settings { prefix: String, numeric_suffix: bool, @@ -210,7 +211,7 @@ struct Settings { filter: Option, strategy: String, strategy_param: String, - verbose: bool, + verbose: bool, // TODO: warning: field is never read: `verbose` } trait Splitter { diff --git a/src/uu/sync/src/sync.rs b/src/uu/sync/src/sync.rs index 985e7580d..59206db98 100644 --- a/src/uu/sync/src/sync.rs +++ b/src/uu/sync/src/sync.rs @@ -199,6 +199,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } } + #[allow(clippy::if_same_then_else)] if matches.is_present(options::FILE_SYSTEM) { #[cfg(any(target_os = "linux", target_os = "windows"))] syncfs(files); diff --git a/src/uu/test/src/parser.rs b/src/uu/test/src/parser.rs index aa44bc5f2..a77bfabb3 100644 --- a/src/uu/test/src/parser.rs +++ b/src/uu/test/src/parser.rs @@ -121,13 +121,7 @@ impl Parser { /// Test if the next token in the stream is a BOOLOP (-a or -o), without /// removing the token from the stream. fn peek_is_boolop(&mut self) -> bool { - // TODO: change to `matches!(self.peek(), Symbol::BoolOp(_))` once MSRV is 1.42 - // #[allow(clippy::match_like_matches_macro)] // needs MSRV 1.43 - if let Symbol::BoolOp(_) = self.peek() { - true - } else { - false - } + matches!(self.peek(), Symbol::BoolOp(_)) } /// Parse an expression. @@ -271,11 +265,10 @@ impl Parser { fn boolop(&mut self, op: Symbol) { if op == Symbol::BoolOp(OsString::from("-a")) { self.term(); - self.stack.push(op); } else { self.expr(); - self.stack.push(op); } + self.stack.push(op); } /// Parse a (possible) unary argument test (string length or file diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index b158fdc0e..00b936e55 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -145,14 +145,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 { || matches.is_present(options::sources::CURRENT) { let timestamp = if matches.is_present(options::sources::DATE) { - parse_date(matches.value_of(options::sources::DATE).unwrap().as_ref()) + parse_date(matches.value_of(options::sources::DATE).unwrap()) } else { - parse_timestamp( - matches - .value_of(options::sources::CURRENT) - .unwrap() - .as_ref(), - ) + parse_timestamp(matches.value_of(options::sources::CURRENT).unwrap()) }; (timestamp, timestamp) } else { diff --git a/src/uu/tr/src/expand.rs b/src/uu/tr/src/expand.rs index 73612a065..7d0c61c30 100644 --- a/src/uu/tr/src/expand.rs +++ b/src/uu/tr/src/expand.rs @@ -110,7 +110,7 @@ impl<'a> Iterator for ExpandSet<'a> { fn next(&mut self) -> Option { // while the Range has elements, try to return chars from it // but make sure that they actually turn out to be Chars! - while let Some(n) = self.range.next() { + for n in &mut self.range { if let Some(c) = from_u32(n) { return Some(c); } diff --git a/src/uu/truncate/src/truncate.rs b/src/uu/truncate/src/truncate.rs index 86a0c9ffc..cada97126 100644 --- a/src/uu/truncate/src/truncate.rs +++ b/src/uu/truncate/src/truncate.rs @@ -15,18 +15,42 @@ use std::convert::TryFrom; use std::fs::{metadata, OpenOptions}; use std::io::ErrorKind; use std::path::Path; -use uucore::parse_size::parse_size; +use uucore::parse_size::{parse_size, ParseSizeError}; -#[derive(Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq)] enum TruncateMode { - Absolute, - Reference, - Extend, - Reduce, - AtMost, - AtLeast, - RoundDown, - RoundUp, + Absolute(usize), + Extend(usize), + Reduce(usize), + AtMost(usize), + AtLeast(usize), + RoundDown(usize), + RoundUp(usize), +} + +impl TruncateMode { + /// Compute a target size in bytes for this truncate mode. + /// + /// `fsize` is the size of the reference file, in bytes. + /// + /// # Examples + /// + /// ```rust,ignore + /// let mode = TruncateMode::Extend(5); + /// let fsize = 10; + /// assert_eq!(mode.to_size(fsize), 15); + /// ``` + fn to_size(&self, fsize: usize) -> usize { + match self { + TruncateMode::Absolute(modsize) => *modsize, + TruncateMode::Extend(modsize) => fsize + modsize, + TruncateMode::Reduce(modsize) => fsize - modsize, + TruncateMode::AtMost(modsize) => fsize.min(*modsize), + TruncateMode::AtLeast(modsize) => fsize.max(*modsize), + TruncateMode::RoundDown(modsize) => fsize - fsize % modsize, + TruncateMode::RoundUp(modsize) => fsize + fsize % modsize, + } + } } static ABOUT: &str = "Shrink or extend the size of each file to the specified size."; @@ -118,112 +142,222 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let no_create = matches.is_present(options::NO_CREATE); let reference = matches.value_of(options::REFERENCE).map(String::from); let size = matches.value_of(options::SIZE).map(String::from); - if reference.is_none() && size.is_none() { - crash!(1, "you must specify either --reference or --size"); - } else { - truncate(no_create, io_blocks, reference, size, files); + if let Err(e) = truncate(no_create, io_blocks, reference, size, files) { + match e.kind() { + ErrorKind::NotFound => { + // TODO Improve error-handling so that the error + // returned by `truncate()` provides the necessary + // parameter for formatting the error message. + let reference = matches.value_of(options::REFERENCE).map(String::from); + crash!( + 1, + "cannot stat '{}': No such file or directory", + reference.unwrap() + ); + } + _ => crash!(1, "{}", e.to_string()), + } } } 0 } +/// Truncate the named file to the specified size. +/// +/// If `create` is true, then the file will be created if it does not +/// already exist. If `size` is larger than the number of bytes in the +/// file, then the file will be padded with zeros. If `size` is smaller +/// than the number of bytes in the file, then the file will be +/// truncated and any bytes beyond `size` will be lost. +/// +/// # Errors +/// +/// If the file could not be opened, or there was a problem setting the +/// size of the file. +fn file_truncate(filename: &str, create: bool, size: usize) -> std::io::Result<()> { + let path = Path::new(filename); + let f = OpenOptions::new().write(true).create(create).open(path)?; + f.set_len(u64::try_from(size).unwrap()) +} + +/// Truncate files to a size relative to a given file. +/// +/// `rfilename` is the name of the reference file. +/// +/// `size_string` gives the size relative to the reference file to which +/// to set the target files. For example, "+3K" means "set each file to +/// be three kilobytes larger than the size of the reference file". +/// +/// If `create` is true, then each file will be created if it does not +/// already exist. +/// +/// # Errors +/// +/// If the any file could not be opened, or there was a problem setting +/// the size of at least one file. +fn truncate_reference_and_size( + rfilename: &str, + size_string: &str, + filenames: Vec, + create: bool, +) -> std::io::Result<()> { + let mode = match parse_mode_and_size(size_string) { + Ok(m) => match m { + TruncateMode::Absolute(_) => { + crash!(1, "you must specify a relative ‘--size’ with ‘--reference’") + } + _ => m, + }, + // TODO: handle errors ParseFailure(String)/SizeTooBig(String) + Err(_) => crash!(1, "Invalid number: ‘{}’", size_string), + }; + let fsize = usize::try_from(metadata(rfilename)?.len()).unwrap(); + let tsize = mode.to_size(fsize); + for filename in &filenames { + file_truncate(filename, create, tsize)?; + } + Ok(()) +} + +/// Truncate files to match the size of a given reference file. +/// +/// `rfilename` is the name of the reference file. +/// +/// If `create` is true, then each file will be created if it does not +/// already exist. +/// +/// # Errors +/// +/// If the any file could not be opened, or there was a problem setting +/// the size of at least one file. +fn truncate_reference_file_only( + rfilename: &str, + filenames: Vec, + create: bool, +) -> std::io::Result<()> { + let tsize = usize::try_from(metadata(rfilename)?.len()).unwrap(); + for filename in &filenames { + file_truncate(filename, create, tsize)?; + } + Ok(()) +} + +/// Truncate files to a specified size. +/// +/// `size_string` gives either an absolute size or a relative size. A +/// relative size adjusts the size of each file relative to its current +/// size. For example, "3K" means "set each file to be three kilobytes" +/// whereas "+3K" means "set each file to be three kilobytes larger than +/// its current size". +/// +/// If `create` is true, then each file will be created if it does not +/// already exist. +/// +/// # Errors +/// +/// If the any file could not be opened, or there was a problem setting +/// the size of at least one file. +fn truncate_size_only( + size_string: &str, + filenames: Vec, + create: bool, +) -> std::io::Result<()> { + let mode = match parse_mode_and_size(size_string) { + Ok(m) => m, + Err(_) => crash!(1, "Invalid number: ‘{}’", size_string), + }; + for filename in &filenames { + let fsize = usize::try_from(metadata(filename)?.len()).unwrap(); + let tsize = mode.to_size(fsize); + file_truncate(filename, create, tsize)?; + } + Ok(()) +} + fn truncate( no_create: bool, _: bool, reference: Option, size: Option, filenames: Vec, -) { - let (modsize, mode) = match size { - Some(size_string) => { - // Trim any whitespace. - let size_string = size_string.trim(); - - // Get the modifier character from the size string, if any. For - // example, if the argument is "+123", then the modifier is '+'. - let c = size_string.chars().next().unwrap(); - - let mode = match c { - '+' => TruncateMode::Extend, - '-' => TruncateMode::Reduce, - '<' => TruncateMode::AtMost, - '>' => TruncateMode::AtLeast, - '/' => TruncateMode::RoundDown, - '%' => TruncateMode::RoundUp, - _ => TruncateMode::Absolute, /* assume that the size is just a number */ - }; - - // If there was a modifier character, strip it. - let size_string = match mode { - TruncateMode::Absolute => size_string, - _ => &size_string[1..], - }; - let num_bytes = match parse_size(size_string) { - Ok(b) => b, - Err(e) => crash!(1, "Invalid number: {}", e.to_string()), - }; - (num_bytes, mode) - } - None => (0, TruncateMode::Reference), - }; - - let refsize: usize = match reference { - Some(ref rfilename) => { - match mode { - // Only Some modes work with a reference - TruncateMode::Reference => (), //No --size was given - TruncateMode::Extend => (), - TruncateMode::Reduce => (), - _ => crash!(1, "you must specify a relative ‘--size’ with ‘--reference’"), - }; - match metadata(rfilename) { - Ok(meta) => usize::try_from(meta.len()).unwrap(), - Err(f) => match f.kind() { - ErrorKind::NotFound => { - crash!(1, "cannot stat '{}': No such file or directory", rfilename) - } - _ => crash!(1, "{}", f.to_string()), - }, - } - } - None => 0, - }; - for filename in &filenames { - let path = Path::new(filename); - match OpenOptions::new() - .read(true) - .write(true) - .create(!no_create) - .open(path) - { - Ok(file) => { - let fsize = match reference { - Some(_) => refsize, - None => match metadata(filename) { - Ok(meta) => usize::try_from(meta.len()).unwrap(), - Err(f) => { - show_warning!("{}", f.to_string()); - continue; - } - }, - }; - let tsize: usize = match mode { - TruncateMode::Absolute => modsize, - TruncateMode::Reference => fsize, - TruncateMode::Extend => fsize + modsize, - TruncateMode::Reduce => fsize - modsize, - TruncateMode::AtMost => fsize.min(modsize), - TruncateMode::AtLeast => fsize.max(modsize), - TruncateMode::RoundDown => fsize - fsize % modsize, - TruncateMode::RoundUp => fsize + fsize % modsize, - }; - match file.set_len(u64::try_from(tsize).unwrap()) { - Ok(_) => {} - Err(f) => crash!(1, "{}", f.to_string()), - }; - } - Err(f) => crash!(1, "{}", f.to_string()), +) -> std::io::Result<()> { + let create = !no_create; + // There are four possibilities + // - reference file given and size given, + // - reference file given but no size given, + // - no reference file given but size given, + // - no reference file given and no size given, + match (reference, size) { + (Some(rfilename), Some(size_string)) => { + truncate_reference_and_size(&rfilename, &size_string, filenames, create) } + (Some(rfilename), None) => truncate_reference_file_only(&rfilename, filenames, create), + (None, Some(size_string)) => truncate_size_only(&size_string, filenames, create), + (None, None) => crash!(1, "you must specify either --reference or --size"), + } +} + +/// Decide whether a character is one of the size modifiers, like '+' or '<'. +fn is_modifier(c: char) -> bool { + c == '+' || c == '-' || c == '<' || c == '>' || c == '/' || c == '%' +} + +/// Parse a size string with optional modifier symbol as its first character. +/// +/// A size string is as described in [`parse_size`]. The first character +/// of `size_string` might be a modifier symbol, like `'+'` or +/// `'<'`. The first element of the pair returned by this function +/// indicates which modifier symbol was present, or +/// [`TruncateMode::Absolute`] if none. +/// +/// # Panics +/// +/// If `size_string` is empty, or if no number could be parsed from the +/// given string (for example, if the string were `"abc"`). +/// +/// # Examples +/// +/// ```rust,ignore +/// assert_eq!(parse_mode_and_size("+123"), (TruncateMode::Extend, 123)); +/// ``` +fn parse_mode_and_size(size_string: &str) -> Result { + // Trim any whitespace. + let mut size_string = size_string.trim(); + + // Get the modifier character from the size string, if any. For + // example, if the argument is "+123", then the modifier is '+'. + if let Some(c) = size_string.chars().next() { + if is_modifier(c) { + size_string = &size_string[1..]; + } + parse_size(size_string).map(match c { + '+' => TruncateMode::Extend, + '-' => TruncateMode::Reduce, + '<' => TruncateMode::AtMost, + '>' => TruncateMode::AtLeast, + '/' => TruncateMode::RoundDown, + '%' => TruncateMode::RoundUp, + _ => TruncateMode::Absolute, + }) + } else { + Err(ParseSizeError::ParseFailure(size_string.to_string())) + } +} + +#[cfg(test)] +mod tests { + use crate::parse_mode_and_size; + use crate::TruncateMode; + + #[test] + fn test_parse_mode_and_size() { + assert_eq!(parse_mode_and_size("10"), Ok(TruncateMode::Absolute(10))); + assert_eq!(parse_mode_and_size("+10"), Ok(TruncateMode::Extend(10))); + assert_eq!(parse_mode_and_size("-10"), Ok(TruncateMode::Reduce(10))); + assert_eq!(parse_mode_and_size("<10"), Ok(TruncateMode::AtMost(10))); + assert_eq!(parse_mode_and_size(">10"), Ok(TruncateMode::AtLeast(10))); + assert_eq!(parse_mode_and_size("/10"), Ok(TruncateMode::RoundDown(10))); + assert_eq!(parse_mode_and_size("%10"), Ok(TruncateMode::RoundUp(10))); } } diff --git a/src/uu/users/src/users.rs b/src/uu/users/src/users.rs index 4bb628441..99e06c1af 100644 --- a/src/uu/users/src/users.rs +++ b/src/uu/users/src/users.rs @@ -6,19 +6,14 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. -/* last synced with: whoami (GNU coreutils) 8.22 */ -// Allow dead code here in order to keep all fields, constants here, for consistency. -#![allow(dead_code)] - #[macro_use] extern crate uucore; -use uucore::utmpx::*; - use clap::{App, Arg}; +use uucore::utmpx::{self, Utmpx}; -static ABOUT: &str = "Display who is currently logged in, according to FILE."; static VERSION: &str = env!("CARGO_PKG_VERSION"); +static ABOUT: &str = "Print the user names of users currently logged in to the current host"; static ARG_FILES: &str = "files"; @@ -26,13 +21,23 @@ fn get_usage() -> String { format!("{0} [FILE]", executable!()) } +fn get_long_usage() -> String { + format!( + "Output who is currently logged in according to FILE. +If FILE is not specified, use {}. /var/log/wtmp as FILE is common.", + utmpx::DEFAULT_FILE + ) +} + pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); + let after_help = get_long_usage(); let matches = App::new(executable!()) .version(VERSION) .about(ABOUT) .usage(&usage[..]) + .after_help(&after_help[..]) .arg(Arg::with_name(ARG_FILES).takes_value(true).max_values(1)) .get_matches_from(args); @@ -44,7 +49,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let filename = if !files.is_empty() { files[0].as_ref() } else { - DEFAULT_FILE + utmpx::DEFAULT_FILE }; let mut users = Utmpx::iter_all_records() diff --git a/src/uu/who/src/who.rs b/src/uu/who/src/who.rs index 19ae3addb..2cddbf2d0 100644 --- a/src/uu/who/src/who.rs +++ b/src/uu/who/src/who.rs @@ -551,10 +551,11 @@ impl Who { " ?".into() }; - let mut s = ut.host(); - if self.do_lookup { - s = safe_unwrap!(ut.canon_host()); - } + let s = if self.do_lookup { + safe_unwrap!(ut.canon_host()) + } else { + ut.host() + }; let hoststr = if s.is_empty() { s } else { format!("({})", s) }; self.print_line( diff --git a/src/uucore/src/lib/features/fsext.rs b/src/uucore/src/lib/features/fsext.rs index 19c634b0b..6343ecd50 100644 --- a/src/uucore/src/lib/features/fsext.rs +++ b/src/uucore/src/lib/features/fsext.rs @@ -179,7 +179,7 @@ impl MountInfo { /* for Irix 6.5 */ | "ignore" => self.dummy = true, _ => self.dummy = self.fs_type == "none" - && self.mount_option.find(MOUNT_OPT_BIND).is_none(), + && !self.mount_option.contains(MOUNT_OPT_BIND) } // set MountInfo::remote #[cfg(windows)] diff --git a/src/uucore/src/lib/features/process.rs b/src/uucore/src/lib/features/process.rs index 078b782f5..975123cf7 100644 --- a/src/uucore/src/lib/features/process.rs +++ b/src/uucore/src/lib/features/process.rs @@ -40,6 +40,7 @@ pub enum ExitStatus { Signal(i32), } +#[allow(clippy::trivially_copy_pass_by_ref)] impl ExitStatus { fn from_std_status(status: StdExitStatus) -> Self { #[cfg(unix)] diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index 69819fd05..c154f23c2 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -148,15 +148,16 @@ pub trait Args: Iterator + Sized { InvalidEncodingHandling::Ignore => s.is_ok(), _ => true, }) - .map(|s| match s.is_ok() { - true => s.unwrap(), - false => s.unwrap_err(), + .map(|s| match s { + Ok(v) => v, + Err(e) => e, }) .collect(); - match full_conversion { - true => ConversionResult::Complete(result_vector), - false => ConversionResult::Lossy(result_vector), + if full_conversion { + ConversionResult::Complete(result_vector) + } else { + ConversionResult::Lossy(result_vector) } } diff --git a/src/uucore/src/lib/mods/backup_control.rs b/src/uucore/src/lib/mods/backup_control.rs index 6004ae84d..83268d351 100644 --- a/src/uucore/src/lib/mods/backup_control.rs +++ b/src/uucore/src/lib/mods/backup_control.rs @@ -33,7 +33,7 @@ pub fn determine_backup_suffix(supplied_suffix: Option<&str>) -> String { if let Some(suffix) = supplied_suffix { String::from(suffix) } else { - env::var("SIMPLE_BACKUP_SUFFIX").unwrap_or("~".to_owned()) + env::var("SIMPLE_BACKUP_SUFFIX").unwrap_or_else(|_| "~".to_owned()) } } diff --git a/src/uucore_procs/src/lib.rs b/src/uucore_procs/src/lib.rs index 10368a5bd..e0d247c3f 100644 --- a/src/uucore_procs/src/lib.rs +++ b/src/uucore_procs/src/lib.rs @@ -1,6 +1,3 @@ -#![allow(dead_code)] // work-around for GH:rust-lang/rust#62127; maint: can be removed when MinSRV >= v1.38.0 -#![allow(unused_macros)] // work-around for GH:rust-lang/rust#62127; maint: can be removed when MinSRV >= v1.38.0 - // Copyright (C) ~ Roy Ivy III ; MIT license extern crate proc_macro; @@ -44,7 +41,6 @@ impl syn::parse::Parse for Tokens { } #[proc_macro] -#[cfg(not(test))] // work-around for GH:rust-lang/rust#62127; maint: can be removed when MinSRV >= v1.38.0 pub fn main(stream: proc_macro::TokenStream) -> proc_macro::TokenStream { let Tokens { expr } = syn::parse_macro_input!(stream as Tokens); proc_dbg!(&expr); diff --git a/tests/by-util/test_base32.rs b/tests/by-util/test_base32.rs index e36c376be..788b85efa 100644 --- a/tests/by-util/test_base32.rs +++ b/tests/by-util/test_base32.rs @@ -34,7 +34,7 @@ fn test_base32_encode_file() { #[test] fn test_decode() { - for decode_param in vec!["-d", "--decode"] { + for decode_param in &["-d", "--decode"] { let input = "JBSWY3DPFQQFO33SNRSCC===\n"; new_ucmd!() .arg(decode_param) @@ -56,7 +56,7 @@ fn test_garbage() { #[test] fn test_ignore_garbage() { - for ignore_garbage_param in vec!["-i", "--ignore-garbage"] { + for ignore_garbage_param in &["-i", "--ignore-garbage"] { let input = "JBSWY\x013DPFQ\x02QFO33SNRSCC===\n"; new_ucmd!() .arg("-d") @@ -69,7 +69,7 @@ fn test_ignore_garbage() { #[test] fn test_wrap() { - for wrap_param in vec!["-w", "--wrap"] { + for wrap_param in &["-w", "--wrap"] { let input = "The quick brown fox jumps over the lazy dog."; new_ucmd!() .arg(wrap_param) @@ -84,16 +84,21 @@ fn test_wrap() { #[test] fn test_wrap_no_arg() { - for wrap_param in vec!["-w", "--wrap"] { - new_ucmd!().arg(wrap_param).fails().stderr_only(format!( - "error: The argument '--wrap \' requires a value but none was supplied\n\nUSAGE:\n base32 [OPTION]... [FILE]\n\nFor more information try --help" - )); + for wrap_param in &["-w", "--wrap"] { + let expected_stderr = "error: The argument '--wrap \' requires a value but none was \ + supplied\n\nUSAGE:\n base32 [OPTION]... [FILE]\n\nFor more \ + information try --help" + .to_string(); + new_ucmd!() + .arg(wrap_param) + .fails() + .stderr_only(expected_stderr); } } #[test] fn test_wrap_bad_arg() { - for wrap_param in vec!["-w", "--wrap"] { + for wrap_param in &["-w", "--wrap"] { new_ucmd!() .arg(wrap_param) .arg("b") diff --git a/tests/by-util/test_base64.rs b/tests/by-util/test_base64.rs index 89405d791..75445c933 100644 --- a/tests/by-util/test_base64.rs +++ b/tests/by-util/test_base64.rs @@ -26,7 +26,7 @@ fn test_base64_encode_file() { #[test] fn test_decode() { - for decode_param in vec!["-d", "--decode"] { + for decode_param in &["-d", "--decode"] { let input = "aGVsbG8sIHdvcmxkIQ=="; new_ucmd!() .arg(decode_param) @@ -48,7 +48,7 @@ fn test_garbage() { #[test] fn test_ignore_garbage() { - for ignore_garbage_param in vec!["-i", "--ignore-garbage"] { + for ignore_garbage_param in &["-i", "--ignore-garbage"] { let input = "aGVsbG8sIHdvcmxkIQ==\0"; new_ucmd!() .arg("-d") @@ -61,7 +61,7 @@ fn test_ignore_garbage() { #[test] fn test_wrap() { - for wrap_param in vec!["-w", "--wrap"] { + for wrap_param in &["-w", "--wrap"] { let input = "The quick brown fox jumps over the lazy dog."; new_ucmd!() .arg(wrap_param) @@ -74,7 +74,7 @@ fn test_wrap() { #[test] fn test_wrap_no_arg() { - for wrap_param in vec!["-w", "--wrap"] { + for wrap_param in &["-w", "--wrap"] { new_ucmd!().arg(wrap_param).fails().stderr_contains( &"The argument '--wrap ' requires a value but none was supplied", ); @@ -83,7 +83,7 @@ fn test_wrap_no_arg() { #[test] fn test_wrap_bad_arg() { - for wrap_param in vec!["-w", "--wrap"] { + for wrap_param in &["-w", "--wrap"] { new_ucmd!() .arg(wrap_param) .arg("b") diff --git a/tests/by-util/test_basename.rs b/tests/by-util/test_basename.rs index 1d26a922a..50d22b2eb 100644 --- a/tests/by-util/test_basename.rs +++ b/tests/by-util/test_basename.rs @@ -4,7 +4,7 @@ use std::ffi::OsStr; #[test] fn test_help() { - for help_flg in vec!["-h", "--help"] { + for help_flg in &["-h", "--help"] { new_ucmd!() .arg(&help_flg) .succeeds() @@ -15,7 +15,7 @@ fn test_help() { #[test] fn test_version() { - for version_flg in vec!["-V", "--version"] { + for version_flg in &["-V", "--version"] { assert!(new_ucmd!() .arg(&version_flg) .succeeds() @@ -59,7 +59,7 @@ fn test_dont_remove_suffix() { #[test] fn test_multiple_param() { - for multiple_param in vec!["-a", "--multiple"] { + for &multiple_param in &["-a", "--multiple"] { let path = "/foo/bar/baz"; new_ucmd!() .args(&[multiple_param, path, path]) @@ -70,7 +70,7 @@ fn test_multiple_param() { #[test] fn test_suffix_param() { - for suffix_param in vec!["-s", "--suffix"] { + for &suffix_param in &["-s", "--suffix"] { let path = "/foo/bar/baz.exe"; new_ucmd!() .args(&[suffix_param, ".exe", path, path]) @@ -81,7 +81,7 @@ fn test_suffix_param() { #[test] fn test_zero_param() { - for zero_param in vec!["-z", "--zero"] { + for &zero_param in &["-z", "--zero"] { let path = "/foo/bar/baz"; new_ucmd!() .args(&[zero_param, "-a", path, path]) @@ -91,7 +91,12 @@ fn test_zero_param() { } fn expect_error(input: Vec<&str>) { - assert!(new_ucmd!().args(&input).fails().no_stdout().stderr().len() > 0); + assert!(!new_ucmd!() + .args(&input) + .fails() + .no_stdout() + .stderr_str() + .is_empty()); } #[test] diff --git a/tests/by-util/test_cat.rs b/tests/by-util/test_cat.rs index 4bb673b95..8ea5bbaae 100644 --- a/tests/by-util/test_cat.rs +++ b/tests/by-util/test_cat.rs @@ -237,7 +237,7 @@ fn test_numbered_lines_no_trailing_newline() { #[test] fn test_stdin_show_nonprinting() { - for same_param in vec!["-v", "--show-nonprinting"] { + for same_param in &["-v", "--show-nonprinting"] { new_ucmd!() .args(&[same_param]) .pipe_in("\t\0\n") @@ -248,7 +248,7 @@ fn test_stdin_show_nonprinting() { #[test] fn test_stdin_show_tabs() { - for same_param in vec!["-T", "--show-tabs"] { + for same_param in &["-T", "--show-tabs"] { new_ucmd!() .args(&[same_param]) .pipe_in("\t\0\n") @@ -259,7 +259,7 @@ fn test_stdin_show_tabs() { #[test] fn test_stdin_show_ends() { - for same_param in vec!["-E", "--show-ends"] { + for &same_param in &["-E", "--show-ends"] { new_ucmd!() .args(&[same_param, "-"]) .pipe_in("\t\0\n\t") @@ -270,7 +270,7 @@ fn test_stdin_show_ends() { #[test] fn test_stdin_show_all() { - for same_param in vec!["-A", "--show-all"] { + for same_param in &["-A", "--show-all"] { new_ucmd!() .args(&[same_param]) .pipe_in("\t\0\n") @@ -299,7 +299,7 @@ fn test_stdin_nonprinting_and_tabs() { #[test] fn test_stdin_squeeze_blank() { - for same_param in vec!["-s", "--squeeze-blank"] { + for same_param in &["-s", "--squeeze-blank"] { new_ucmd!() .arg(same_param) .pipe_in("\n\na\n\n\n\n\nb\n\n\n") @@ -310,7 +310,7 @@ fn test_stdin_squeeze_blank() { #[test] fn test_stdin_number_non_blank() { - for same_param in vec!["-b", "--number-nonblank"] { + for same_param in &["-b", "--number-nonblank"] { new_ucmd!() .arg(same_param) .arg("-") @@ -322,7 +322,7 @@ fn test_stdin_number_non_blank() { #[test] fn test_non_blank_overrides_number() { - for same_param in vec!["-b", "--number-nonblank"] { + for &same_param in &["-b", "--number-nonblank"] { new_ucmd!() .args(&[same_param, "-"]) .pipe_in("\na\nb\n\n\nc") @@ -333,7 +333,7 @@ fn test_non_blank_overrides_number() { #[test] fn test_squeeze_blank_before_numbering() { - for same_param in vec!["-s", "--squeeze-blank"] { + for &same_param in &["-s", "--squeeze-blank"] { new_ucmd!() .args(&[same_param, "-n", "-"]) .pipe_in("a\n\n\nb") @@ -406,10 +406,12 @@ fn test_domain_socket() { use std::io::prelude::*; use std::sync::{Arc, Barrier}; use std::thread; - use tempdir::TempDir; use unix_socket::UnixListener; - let dir = TempDir::new("unix_socket").expect("failed to create dir"); + let dir = tempfile::Builder::new() + .prefix("unix_socket") + .tempdir() + .expect("failed to create dir"); let socket_path = dir.path().join("sock"); let listener = UnixListener::bind(&socket_path).expect("failed to create socket"); @@ -427,7 +429,7 @@ fn test_domain_socket() { let child = new_ucmd!().args(&[socket_path]).run_no_wait(); barrier.wait(); - let stdout = &child.wait_with_output().unwrap().stdout.clone(); + let stdout = &child.wait_with_output().unwrap().stdout; let output = String::from_utf8_lossy(&stdout); assert_eq!("a\tb", output); diff --git a/tests/by-util/test_chgrp.rs b/tests/by-util/test_chgrp.rs index a7848b1b6..c0fc503ae 100644 --- a/tests/by-util/test_chgrp.rs +++ b/tests/by-util/test_chgrp.rs @@ -6,7 +6,7 @@ fn test_invalid_option() { new_ucmd!().arg("-w").arg("/").fails(); } -static DIR: &'static str = "/tmp"; +static DIR: &str = "/tmp"; #[test] fn test_invalid_group() { diff --git a/tests/by-util/test_chmod.rs b/tests/by-util/test_chmod.rs index f20429a6e..4611d1b96 100644 --- a/tests/by-util/test_chmod.rs +++ b/tests/by-util/test_chmod.rs @@ -8,8 +8,8 @@ use self::chmod::strip_minus_from_mode; extern crate chmod; use self::libc::umask; -static TEST_FILE: &'static str = "file"; -static REFERENCE_FILE: &'static str = "reference"; +static TEST_FILE: &str = "file"; +static REFERENCE_FILE: &str = "reference"; static REFERENCE_PERMS: u32 = 0o247; lazy_static! { static ref UMASK_MUTEX: Mutex<()> = Mutex::new(()); @@ -69,6 +69,7 @@ fn run_tests(tests: Vec) { } #[test] +#[allow(clippy::unreadable_literal)] fn test_chmod_octal() { let tests = vec![ TestCase { @@ -121,6 +122,7 @@ fn test_chmod_octal() { } #[test] +#[allow(clippy::unreadable_literal)] fn test_chmod_ugoa() { let _guard = UMASK_MUTEX.lock(); @@ -216,6 +218,7 @@ fn test_chmod_ugoa() { } #[test] +#[allow(clippy::unreadable_literal)] fn test_chmod_ugo_copy() { let tests = vec![ TestCase { @@ -248,6 +251,7 @@ fn test_chmod_ugo_copy() { } #[test] +#[allow(clippy::unreadable_literal)] fn test_chmod_many_options() { let _guard = UMASK_MUTEX.lock(); @@ -264,6 +268,7 @@ fn test_chmod_many_options() { } #[test] +#[allow(clippy::unreadable_literal)] fn test_chmod_reference_file() { let tests = vec![ TestCase { @@ -303,6 +308,7 @@ fn test_permission_denied() { } #[test] +#[allow(clippy::unreadable_literal)] fn test_chmod_recursive() { let _guard = UMASK_MUTEX.lock(); @@ -477,7 +483,7 @@ fn test_chmod_strip_minus_from_mode() { ]; for test in tests { - let mut args: Vec = test.0.split(" ").map(|v| v.to_string()).collect(); + let mut args: Vec = test.0.split(' ').map(|v| v.to_string()).collect(); let _mode_had_minus_prefix = strip_minus_from_mode(&mut args); assert_eq!(test.1, args.join(" ")); } diff --git a/tests/by-util/test_chown.rs b/tests/by-util/test_chown.rs index 3d94632a6..a531fc7f3 100644 --- a/tests/by-util/test_chown.rs +++ b/tests/by-util/test_chown.rs @@ -39,7 +39,7 @@ mod test_passgrp { #[test] fn test_usr2uid() { assert_eq!(0, usr2uid("root").unwrap()); - assert!(usr2uid("88888888").is_err()); + assert!(usr2uid("88_888_888").is_err()); assert!(usr2uid("auserthatdoesntexist").is_err()); } @@ -50,14 +50,14 @@ mod test_passgrp { } else { assert_eq!(0, grp2gid("wheel").unwrap()); } - assert!(grp2gid("88888888").is_err()); + assert!(grp2gid("88_888_888").is_err()); assert!(grp2gid("agroupthatdoesntexist").is_err()); } #[test] fn test_uid2usr() { assert_eq!("root", uid2usr(0).unwrap()); - assert!(uid2usr(88888888).is_err()); + assert!(uid2usr(88_888_888).is_err()); } #[test] @@ -67,7 +67,7 @@ mod test_passgrp { } else { assert_eq!("wheel", gid2grp(0).unwrap()); } - assert!(gid2grp(88888888).is_err()); + assert!(gid2grp(88_888_888).is_err()); } } diff --git a/tests/by-util/test_cksum.rs b/tests/by-util/test_cksum.rs index 81ef4c177..0fd028781 100644 --- a/tests/by-util/test_cksum.rs +++ b/tests/by-util/test_cksum.rs @@ -85,12 +85,12 @@ fn test_crc_for_bigger_than_32_bytes() { let result = ucmd.arg("chars.txt").succeeds(); - let mut stdout_splitted = result.stdout_str().split(" "); + let mut stdout_splitted = result.stdout_str().split(' '); let cksum: i64 = stdout_splitted.next().unwrap().parse().unwrap(); let bytes_cnt: i64 = stdout_splitted.next().unwrap().parse().unwrap(); - assert_eq!(cksum, 586047089); + assert_eq!(cksum, 586_047_089); assert_eq!(bytes_cnt, 16); } @@ -100,11 +100,11 @@ fn test_stdin_larger_than_128_bytes() { let result = ucmd.arg("larger_than_2056_bytes.txt").succeeds(); - let mut stdout_splitted = result.stdout_str().split(" "); + let mut stdout_splitted = result.stdout_str().split(' '); let cksum: i64 = stdout_splitted.next().unwrap().parse().unwrap(); let bytes_cnt: i64 = stdout_splitted.next().unwrap().parse().unwrap(); - assert_eq!(cksum, 945881979); + assert_eq!(cksum, 945_881_979); assert_eq!(bytes_cnt, 2058); } diff --git a/tests/by-util/test_comm.rs b/tests/by-util/test_comm.rs index 23aeb8309..fa8c8beca 100644 --- a/tests/by-util/test_comm.rs +++ b/tests/by-util/test_comm.rs @@ -74,7 +74,7 @@ fn output_delimiter_require_arg() { #[cfg_attr(not(feature = "test_unimplemented"), ignore)] #[test] fn zero_terminated() { - for param in vec!["-z", "--zero-terminated"] { + for ¶m in &["-z", "--zero-terminated"] { new_ucmd!() .args(&[param, "a", "b"]) .fails() diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index d41d3f6ed..e4d7fdea7 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -108,7 +108,7 @@ fn test_cp_multiple_files() { #[test] // FixME: for MacOS, this has intermittent failures; track repair progress at GH:uutils/coreutils/issues/1590 -#[cfg(not(macos))] +#[cfg(not(target_os = "macos"))] fn test_cp_recurse() { let (at, mut ucmd) = at_and_ucmd!(); ucmd.arg("-r") @@ -132,7 +132,7 @@ fn test_cp_with_dirs_t() { #[test] // FixME: for MacOS, this has intermittent failures; track repair progress at GH:uutils/coreutils/issues/1590 -#[cfg(not(macos))] +#[cfg(not(target_os = "macos"))] fn test_cp_with_dirs() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; @@ -316,6 +316,22 @@ fn test_cp_arg_backup() { ); } +#[test] +fn test_cp_arg_backup_with_other_args() { + let (at, mut ucmd) = at_and_ucmd!(); + + ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg(TEST_HOW_ARE_YOU_SOURCE) + .arg("-vbL") + .succeeds(); + + assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n"); + assert_eq!( + at.read(&*format!("{}~", TEST_HOW_ARE_YOU_SOURCE)), + "How are you?\n" + ); +} + #[test] fn test_cp_arg_backup_arg_first() { let (at, mut ucmd) = at_and_ucmd!(); diff --git a/tests/by-util/test_cut.rs b/tests/by-util/test_cut.rs index 413b73154..8f81b94c1 100644 --- a/tests/by-util/test_cut.rs +++ b/tests/by-util/test_cut.rs @@ -1,13 +1,13 @@ use crate::common::util::*; -static INPUT: &'static str = "lists.txt"; +static INPUT: &str = "lists.txt"; struct TestedSequence<'b> { name: &'b str, sequence: &'b str, } -static EXAMPLE_SEQUENCES: &'static [TestedSequence<'static>] = &[ +static EXAMPLE_SEQUENCES: &[TestedSequence] = &[ TestedSequence { name: "singular", sequence: "2", @@ -34,14 +34,14 @@ static EXAMPLE_SEQUENCES: &'static [TestedSequence<'static>] = &[ }, ]; -static COMPLEX_SEQUENCE: &'static TestedSequence<'static> = &TestedSequence { +static COMPLEX_SEQUENCE: &TestedSequence = &TestedSequence { name: "", sequence: "9-,6-7,-2,4", }; #[test] fn test_byte_sequence() { - for param in vec!["-b", "--bytes"] { + for ¶m in &["-b", "--bytes"] { for example_seq in EXAMPLE_SEQUENCES { new_ucmd!() .args(&[param, example_seq.sequence, INPUT]) @@ -53,7 +53,7 @@ fn test_byte_sequence() { #[test] fn test_char_sequence() { - for param in vec!["-c", "--characters"] { + for ¶m in &["-c", "--characters"] { for example_seq in EXAMPLE_SEQUENCES { //as of coreutils 8.25 a char range is effectively the same as a byte range; there is no distinct treatment of utf8 chars. new_ucmd!() @@ -66,7 +66,7 @@ fn test_char_sequence() { #[test] fn test_field_sequence() { - for param in vec!["-f", "--fields"] { + for ¶m in &["-f", "--fields"] { for example_seq in EXAMPLE_SEQUENCES { new_ucmd!() .args(&[param, example_seq.sequence, INPUT]) @@ -78,7 +78,7 @@ fn test_field_sequence() { #[test] fn test_specify_delimiter() { - for param in vec!["-d", "--delimiter"] { + for ¶m in &["-d", "--delimiter"] { new_ucmd!() .args(&[param, ":", "-f", COMPLEX_SEQUENCE.sequence, INPUT]) .succeeds() @@ -122,7 +122,7 @@ fn test_zero_terminated() { #[test] fn test_only_delimited() { - for param in vec!["-s", "--only-delimited"] { + for param in &["-s", "--only-delimited"] { new_ucmd!() .args(&["-d_", param, "-f", "1"]) .pipe_in("91\n82\n7_3") diff --git a/tests/by-util/test_du.rs b/tests/by-util/test_du.rs index a8a3049f7..9814e6e8c 100644 --- a/tests/by-util/test_du.rs +++ b/tests/by-util/test_du.rs @@ -204,7 +204,7 @@ fn test_du_d_flag() { // TODO: gnu `du` doesn't use trailing "/" here // result.stdout_str(), result_reference.stdout_str() result.stdout_str().trim_end_matches("/\n"), - result_reference.stdout_str().trim_end_matches("\n") + result_reference.stdout_str().trim_end_matches('\n') ); return; } diff --git a/tests/by-util/test_env.rs b/tests/by-util/test_env.rs index e86a41783..23bba57a9 100644 --- a/tests/by-util/test_env.rs +++ b/tests/by-util/test_env.rs @@ -118,7 +118,7 @@ fn test_null_delimiter() { let mut vars: Vec<_> = out.split('\0').collect(); assert_eq!(vars.len(), 3); - vars.sort(); + vars.sort_unstable(); assert_eq!(vars[0], ""); assert_eq!(vars[1], "ABC=xyz"); assert_eq!(vars[2], "FOO=bar"); @@ -135,7 +135,7 @@ fn test_unset_variable() { .succeeds() .stdout_move_str(); - assert_eq!(out.lines().any(|line| line.starts_with("HOME=")), false); + assert!(!out.lines().any(|line| line.starts_with("HOME="))); } #[test] @@ -196,7 +196,7 @@ fn test_change_directory() { fn test_fail_change_directory() { let scene = TestScenario::new(util_name!()); let some_non_existing_path = "some_nonexistent_path"; - assert_eq!(Path::new(some_non_existing_path).is_dir(), false); + assert!(!Path::new(some_non_existing_path).is_dir()); let out = scene .ucmd() diff --git a/tests/by-util/test_expr.rs b/tests/by-util/test_expr.rs index f20739e13..30e3016a3 100644 --- a/tests/by-util/test_expr.rs +++ b/tests/by-util/test_expr.rs @@ -2,55 +2,95 @@ use crate::common::util::*; #[test] fn test_simple_arithmetic() { - new_ucmd!().args(&["1", "+", "1"]).run().stdout_is("2\n"); + new_ucmd!() + .args(&["1", "+", "1"]) + .succeeds() + .stdout_only("2\n"); - new_ucmd!().args(&["1", "-", "1"]).run().stdout_is("0\n"); + new_ucmd!() + .args(&["1", "-", "1"]) + .fails() + .status_code(1) + .stdout_only("0\n"); - new_ucmd!().args(&["3", "*", "2"]).run().stdout_is("6\n"); + new_ucmd!() + .args(&["3", "*", "2"]) + .succeeds() + .stdout_only("6\n"); - new_ucmd!().args(&["4", "/", "2"]).run().stdout_is("2\n"); + new_ucmd!() + .args(&["4", "/", "2"]) + .succeeds() + .stdout_only("2\n"); } #[test] fn test_complex_arithmetic() { - let run = new_ucmd!() + new_ucmd!() .args(&["9223372036854775807", "+", "9223372036854775807"]) - .run(); - run.stdout_is(""); - run.stderr_is("expr: +: Numerical result out of range"); + .succeeds() + .stdout_only("18446744073709551614\n"); - let run = new_ucmd!().args(&["9", "/", "0"]).run(); - run.stdout_is(""); - run.stderr_is("expr: division by zero"); + new_ucmd!() + .args(&[ + "92233720368547758076549841651981984981498415651", + "%", + "922337203685", + ]) + .succeeds() + .stdout_only("533691697086\n"); + + new_ucmd!() + .args(&[ + "92233720368547758076549841651981984981498415651", + "*", + "922337203685", + ]) + .succeeds() + .stdout_only("85070591730190566808700855121818604965830915152801178873935\n"); + + new_ucmd!() + .args(&[ + "92233720368547758076549841651981984981498415651", + "-", + "922337203685", + ]) + .succeeds() + .stdout_only("92233720368547758076549841651981984059161211966\n"); + + new_ucmd!() + .args(&["9", "/", "0"]) + .fails() + .stderr_only("expr: division by zero\n"); } #[test] fn test_parenthesis() { new_ucmd!() .args(&["(", "1", "+", "1", ")", "*", "2"]) - .run() - .stdout_is("4\n"); + .succeeds() + .stdout_only("4\n"); } #[test] fn test_or() { new_ucmd!() .args(&["0", "|", "foo"]) - .run() - .stdout_is("foo\n"); + .succeeds() + .stdout_only("foo\n"); new_ucmd!() .args(&["foo", "|", "bar"]) - .run() - .stdout_is("foo\n"); + .succeeds() + .stdout_only("foo\n"); } #[test] fn test_and() { new_ucmd!() .args(&["foo", "&", "1"]) - .run() - .stdout_is("foo\n"); + .succeeds() + .stdout_only("foo\n"); new_ucmd!().args(&["", "&", "1"]).run().stdout_is("0\n"); } diff --git a/tests/by-util/test_factor.rs b/tests/by-util/test_factor.rs index af2ff4ddb..917d19a49 100644 --- a/tests/by-util/test_factor.rs +++ b/tests/by-util/test_factor.rs @@ -4,6 +4,7 @@ // // For the full copyright and license information, please view the LICENSE file // that was distributed with this source code. +#![allow(clippy::unreadable_literal)] use crate::common::util::*; use std::time::SystemTime; @@ -39,6 +40,18 @@ fn test_first_100000_integers() { assert_eq!(hash_check, "4ed2d8403934fa1c76fe4b84c5d4b8850299c359"); } +#[test] +fn test_cli_args() { + // Make sure that factor works with CLI arguments as well. + new_ucmd!().args(&["3"]).succeeds().stdout_contains("3: 3"); + + new_ucmd!() + .args(&["3", "6"]) + .succeeds() + .stdout_contains("3: 3") + .stdout_contains("6: 2 3"); +} + #[test] fn test_random() { use conv::prelude::*; @@ -77,7 +90,7 @@ fn test_random() { }; } - factors.sort(); + factors.sort_unstable(); (product, factors) }; @@ -92,7 +105,7 @@ fn test_random() { for factor in factors { outstring.push_str(&(format!(" {}", factor))[..]); } - outstring.push_str("\n"); + outstring.push('\n'); } run(instring.as_bytes(), outstring.as_bytes()); @@ -131,7 +144,7 @@ fn test_random_big() { f_bits.push(extrarange.sample(&mut rng)); } f_bits.push(extrabits); - f_bits.sort(); + f_bits.sort_unstable(); // compute sequential differences here. We leave off the +14 bits // so we can just index PRIMES_BY_BITS @@ -160,7 +173,7 @@ fn test_random_big() { } assert_eq!(nbits, 64); - factors.sort(); + factors.sort_unstable(); (product, factors) }; @@ -174,7 +187,7 @@ fn test_random_big() { for factor in factors { outstring.push_str(&(format!(" {}", factor))[..]); } - outstring.push_str("\n"); + outstring.push('\n'); } run(instring.as_bytes(), outstring.as_bytes()); @@ -202,7 +215,7 @@ fn run(instring: &[u8], outstring: &[u8]) { .stdout_is(String::from_utf8(outstring.to_owned()).unwrap()); } -const PRIMES_BY_BITS: &'static [&'static [u64]] = &[ +const PRIMES_BY_BITS: &[&[u64]] = &[ PRIMES14, PRIMES15, PRIMES16, PRIMES17, PRIMES18, PRIMES19, PRIMES20, PRIMES21, PRIMES22, PRIMES23, PRIMES24, PRIMES25, PRIMES26, PRIMES27, PRIMES28, PRIMES29, PRIMES30, PRIMES31, PRIMES32, PRIMES33, PRIMES34, PRIMES35, PRIMES36, PRIMES37, PRIMES38, PRIMES39, PRIMES40, @@ -210,7 +223,7 @@ const PRIMES_BY_BITS: &'static [&'static [u64]] = &[ PRIMES50, ]; -const PRIMES64: &'static [u64] = &[ +const PRIMES64: &[u64] = &[ 18446744073709551557, 18446744073709551533, 18446744073709551521, @@ -262,7 +275,7 @@ const PRIMES64: &'static [u64] = &[ 18446744073709549571, ]; -const PRIMES14: &'static [u64] = &[ +const PRIMES14: &[u64] = &[ 16381, 16369, 16363, 16361, 16349, 16339, 16333, 16319, 16301, 16273, 16267, 16253, 16249, 16231, 16229, 16223, 16217, 16193, 16189, 16187, 16183, 16141, 16139, 16127, 16111, 16103, 16097, 16091, 16087, 16073, 16069, 16067, 16063, 16061, 16057, 16033, 16007, 16001, 15991, @@ -274,7 +287,7 @@ const PRIMES14: &'static [u64] = &[ 15373, ]; -const PRIMES15: &'static [u64] = &[ +const PRIMES15: &[u64] = &[ 32749, 32719, 32717, 32713, 32707, 32693, 32687, 32653, 32647, 32633, 32621, 32611, 32609, 32603, 32587, 32579, 32573, 32569, 32563, 32561, 32537, 32533, 32531, 32507, 32503, 32497, 32491, 32479, 32467, 32443, 32441, 32429, 32423, 32413, 32411, 32401, 32381, 32377, 32371, @@ -285,7 +298,7 @@ const PRIMES15: &'static [u64] = &[ 31847, 31817, 31799, 31793, 31771, 31769, 31751, ]; -const PRIMES16: &'static [u64] = &[ +const PRIMES16: &[u64] = &[ 65521, 65519, 65497, 65479, 65449, 65447, 65437, 65423, 65419, 65413, 65407, 65393, 65381, 65371, 65357, 65353, 65327, 65323, 65309, 65293, 65287, 65269, 65267, 65257, 65239, 65213, 65203, 65183, 65179, 65173, 65171, 65167, 65147, 65141, 65129, 65123, 65119, 65111, 65101, @@ -295,7 +308,7 @@ const PRIMES16: &'static [u64] = &[ 64627, 64621, 64613, 64609, 64601, 64591, 64579, 64577, 64567, 64553, ]; -const PRIMES17: &'static [u64] = &[ +const PRIMES17: &[u64] = &[ 131071, 131063, 131059, 131041, 131023, 131011, 131009, 130987, 130981, 130973, 130969, 130957, 130927, 130873, 130859, 130843, 130841, 130829, 130817, 130811, 130807, 130787, 130783, 130769, 130729, 130699, 130693, 130687, 130681, 130657, 130651, 130649, 130643, 130639, 130633, 130631, @@ -306,7 +319,7 @@ const PRIMES17: &'static [u64] = &[ 130073, 130069, 130057, 130051, ]; -const PRIMES18: &'static [u64] = &[ +const PRIMES18: &[u64] = &[ 262139, 262133, 262127, 262121, 262111, 262109, 262103, 262079, 262069, 262051, 262049, 262027, 262007, 261983, 261977, 261973, 261971, 261959, 261917, 261887, 261881, 261847, 261823, 261799, 261791, 261787, 261773, 261761, 261757, 261739, 261721, 261713, 261707, 261697, 261673, 261643, @@ -316,7 +329,7 @@ const PRIMES18: &'static [u64] = &[ 261169, 261167, 261127, ]; -const PRIMES19: &'static [u64] = &[ +const PRIMES19: &[u64] = &[ 524287, 524269, 524261, 524257, 524243, 524231, 524221, 524219, 524203, 524201, 524197, 524189, 524171, 524149, 524123, 524119, 524113, 524099, 524087, 524081, 524071, 524063, 524057, 524053, 524047, 523997, 523987, 523969, 523949, 523937, 523927, 523907, 523903, 523877, 523867, 523847, @@ -326,7 +339,7 @@ const PRIMES19: &'static [u64] = &[ 523403, 523387, 523357, 523351, 523349, 523333, 523307, 523297, ]; -const PRIMES20: &'static [u64] = &[ +const PRIMES20: &[u64] = &[ 1048573, 1048571, 1048559, 1048549, 1048517, 1048507, 1048447, 1048433, 1048423, 1048391, 1048387, 1048367, 1048361, 1048357, 1048343, 1048309, 1048291, 1048273, 1048261, 1048219, 1048217, 1048213, 1048193, 1048189, 1048139, 1048129, 1048127, 1048123, 1048063, 1048051, @@ -336,7 +349,7 @@ const PRIMES20: &'static [u64] = &[ 1047691, 1047689, 1047671, 1047667, 1047653, 1047649, 1047647, 1047589, 1047587, 1047559, ]; -const PRIMES21: &'static [u64] = &[ +const PRIMES21: &[u64] = &[ 2097143, 2097133, 2097131, 2097097, 2097091, 2097083, 2097047, 2097041, 2097031, 2097023, 2097013, 2096993, 2096987, 2096971, 2096959, 2096957, 2096947, 2096923, 2096911, 2096909, 2096893, 2096881, 2096873, 2096867, 2096851, 2096837, 2096807, 2096791, 2096789, 2096777, @@ -346,7 +359,7 @@ const PRIMES21: &'static [u64] = &[ 2096221, 2096209, 2096191, 2096183, 2096147, ]; -const PRIMES22: &'static [u64] = &[ +const PRIMES22: &[u64] = &[ 4194301, 4194287, 4194277, 4194271, 4194247, 4194217, 4194199, 4194191, 4194187, 4194181, 4194173, 4194167, 4194143, 4194137, 4194131, 4194107, 4194103, 4194023, 4194011, 4194007, 4193977, 4193971, 4193963, 4193957, 4193939, 4193929, 4193909, 4193869, 4193807, 4193803, @@ -356,7 +369,7 @@ const PRIMES22: &'static [u64] = &[ 4193297, ]; -const PRIMES23: &'static [u64] = &[ +const PRIMES23: &[u64] = &[ 8388593, 8388587, 8388581, 8388571, 8388547, 8388539, 8388473, 8388461, 8388451, 8388449, 8388439, 8388427, 8388421, 8388409, 8388377, 8388371, 8388319, 8388301, 8388287, 8388283, 8388277, 8388239, 8388209, 8388187, 8388113, 8388109, 8388091, 8388071, 8388059, 8388019, @@ -365,7 +378,7 @@ const PRIMES23: &'static [u64] = &[ 8387723, 8387707, 8387671, 8387611, 8387609, 8387591, ]; -const PRIMES24: &'static [u64] = &[ +const PRIMES24: &[u64] = &[ 16777213, 16777199, 16777183, 16777153, 16777141, 16777139, 16777127, 16777121, 16777099, 16777049, 16777027, 16776989, 16776973, 16776971, 16776967, 16776961, 16776941, 16776937, 16776931, 16776919, 16776901, 16776899, 16776869, 16776857, 16776839, 16776833, 16776817, @@ -375,7 +388,7 @@ const PRIMES24: &'static [u64] = &[ 16776317, 16776313, 16776289, 16776217, 16776211, ]; -const PRIMES25: &'static [u64] = &[ +const PRIMES25: &[u64] = &[ 33554393, 33554383, 33554371, 33554347, 33554341, 33554317, 33554291, 33554273, 33554267, 33554249, 33554239, 33554221, 33554201, 33554167, 33554159, 33554137, 33554123, 33554093, 33554083, 33554077, 33554051, 33554021, 33554011, 33554009, 33553999, 33553991, 33553969, @@ -385,7 +398,7 @@ const PRIMES25: &'static [u64] = &[ 33553519, 33553517, 33553511, 33553489, 33553463, 33553451, 33553417, ]; -const PRIMES26: &'static [u64] = &[ +const PRIMES26: &[u64] = &[ 67108859, 67108837, 67108819, 67108777, 67108763, 67108757, 67108753, 67108747, 67108739, 67108729, 67108721, 67108709, 67108693, 67108669, 67108667, 67108661, 67108649, 67108633, 67108597, 67108579, 67108529, 67108511, 67108507, 67108493, 67108471, 67108463, 67108453, @@ -396,7 +409,7 @@ const PRIMES26: &'static [u64] = &[ 67107863, ]; -const PRIMES27: &'static [u64] = &[ +const PRIMES27: &[u64] = &[ 134217689, 134217649, 134217617, 134217613, 134217593, 134217541, 134217529, 134217509, 134217497, 134217493, 134217487, 134217467, 134217439, 134217437, 134217409, 134217403, 134217401, 134217367, 134217361, 134217353, 134217323, 134217301, 134217277, 134217257, @@ -407,7 +420,7 @@ const PRIMES27: &'static [u64] = &[ 134216737, 134216729, ]; -const PRIMES28: &'static [u64] = &[ +const PRIMES28: &[u64] = &[ 268435399, 268435367, 268435361, 268435337, 268435331, 268435313, 268435291, 268435273, 268435243, 268435183, 268435171, 268435157, 268435147, 268435133, 268435129, 268435121, 268435109, 268435091, 268435067, 268435043, 268435039, 268435033, 268435019, 268435009, @@ -418,7 +431,7 @@ const PRIMES28: &'static [u64] = &[ 268434479, 268434461, ]; -const PRIMES29: &'static [u64] = &[ +const PRIMES29: &[u64] = &[ 536870909, 536870879, 536870869, 536870849, 536870839, 536870837, 536870819, 536870813, 536870791, 536870779, 536870767, 536870743, 536870729, 536870723, 536870717, 536870701, 536870683, 536870657, 536870641, 536870627, 536870611, 536870603, 536870599, 536870573, @@ -428,7 +441,7 @@ const PRIMES29: &'static [u64] = &[ 536870027, 536869999, 536869951, 536869943, 536869937, 536869919, 536869901, 536869891, ]; -const PRIMES30: &'static [u64] = &[ +const PRIMES30: &[u64] = &[ 1073741789, 1073741783, 1073741741, 1073741723, 1073741719, 1073741717, 1073741689, 1073741671, 1073741663, 1073741651, 1073741621, 1073741567, 1073741561, 1073741527, 1073741503, 1073741477, 1073741467, 1073741441, 1073741419, 1073741399, 1073741387, 1073741381, 1073741371, 1073741329, @@ -437,7 +450,7 @@ const PRIMES30: &'static [u64] = &[ 1073740853, 1073740847, 1073740819, 1073740807, ]; -const PRIMES31: &'static [u64] = &[ +const PRIMES31: &[u64] = &[ 2147483647, 2147483629, 2147483587, 2147483579, 2147483563, 2147483549, 2147483543, 2147483497, 2147483489, 2147483477, 2147483423, 2147483399, 2147483353, 2147483323, 2147483269, 2147483249, 2147483237, 2147483179, 2147483171, 2147483137, 2147483123, 2147483077, 2147483069, 2147483059, @@ -446,7 +459,7 @@ const PRIMES31: &'static [u64] = &[ 2147482763, 2147482739, 2147482697, 2147482693, 2147482681, 2147482663, 2147482661, ]; -const PRIMES32: &'static [u64] = &[ +const PRIMES32: &[u64] = &[ 4294967291, 4294967279, 4294967231, 4294967197, 4294967189, 4294967161, 4294967143, 4294967111, 4294967087, 4294967029, 4294966997, 4294966981, 4294966943, 4294966927, 4294966909, 4294966877, 4294966829, 4294966813, 4294966769, 4294966667, 4294966661, 4294966657, 4294966651, 4294966639, @@ -454,7 +467,7 @@ const PRIMES32: &'static [u64] = &[ 4294966373, 4294966367, 4294966337, 4294966297, ]; -const PRIMES33: &'static [u64] = &[ +const PRIMES33: &[u64] = &[ 8589934583, 8589934567, 8589934543, 8589934513, 8589934487, 8589934307, 8589934291, 8589934289, 8589934271, 8589934237, 8589934211, 8589934207, 8589934201, 8589934187, 8589934151, 8589934141, 8589934139, 8589934117, 8589934103, 8589934099, 8589934091, 8589934069, 8589934049, 8589934027, @@ -463,7 +476,7 @@ const PRIMES33: &'static [u64] = &[ 8589933647, 8589933641, 8589933637, 8589933631, 8589933629, 8589933619, 8589933601, 8589933581, ]; -const PRIMES34: &'static [u64] = &[ +const PRIMES34: &[u64] = &[ 17179869143, 17179869107, 17179869071, @@ -514,7 +527,7 @@ const PRIMES34: &'static [u64] = &[ 17179868183, ]; -const PRIMES35: &'static [u64] = &[ +const PRIMES35: &[u64] = &[ 34359738337, 34359738319, 34359738307, @@ -547,7 +560,7 @@ const PRIMES35: &'static [u64] = &[ 34359737371, ]; -const PRIMES36: &'static [u64] = &[ +const PRIMES36: &[u64] = &[ 68719476731, 68719476719, 68719476713, @@ -599,7 +612,7 @@ const PRIMES36: &'static [u64] = &[ 68719475729, ]; -const PRIMES37: &'static [u64] = &[ +const PRIMES37: &[u64] = &[ 137438953447, 137438953441, 137438953427, @@ -625,7 +638,7 @@ const PRIMES37: &'static [u64] = &[ 137438952491, ]; -const PRIMES38: &'static [u64] = &[ +const PRIMES38: &[u64] = &[ 274877906899, 274877906857, 274877906837, @@ -665,7 +678,7 @@ const PRIMES38: &'static [u64] = &[ 274877905931, ]; -const PRIMES39: &'static [u64] = &[ +const PRIMES39: &[u64] = &[ 549755813881, 549755813869, 549755813821, @@ -711,7 +724,7 @@ const PRIMES39: &'static [u64] = &[ 549755812867, ]; -const PRIMES40: &'static [u64] = &[ +const PRIMES40: &[u64] = &[ 1099511627689, 1099511627609, 1099511627581, @@ -741,7 +754,7 @@ const PRIMES40: &'static [u64] = &[ 1099511626771, ]; -const PRIMES41: &'static [u64] = &[ +const PRIMES41: &[u64] = &[ 2199023255531, 2199023255521, 2199023255497, @@ -780,7 +793,7 @@ const PRIMES41: &'static [u64] = &[ 2199023254567, ]; -const PRIMES42: &'static [u64] = &[ +const PRIMES42: &[u64] = &[ 4398046511093, 4398046511087, 4398046511071, @@ -820,7 +833,7 @@ const PRIMES42: &'static [u64] = &[ 4398046510093, ]; -const PRIMES43: &'static [u64] = &[ +const PRIMES43: &[u64] = &[ 8796093022151, 8796093022141, 8796093022091, @@ -855,7 +868,7 @@ const PRIMES43: &'static [u64] = &[ 8796093021269, ]; -const PRIMES44: &'static [u64] = &[ +const PRIMES44: &[u64] = &[ 17592186044399, 17592186044299, 17592186044297, @@ -888,7 +901,7 @@ const PRIMES44: &'static [u64] = &[ 17592186043409, ]; -const PRIMES45: &'static [u64] = &[ +const PRIMES45: &[u64] = &[ 35184372088777, 35184372088763, 35184372088751, @@ -929,7 +942,7 @@ const PRIMES45: &'static [u64] = &[ 35184372087869, ]; -const PRIMES46: &'static [u64] = &[ +const PRIMES46: &[u64] = &[ 70368744177643, 70368744177607, 70368744177601, @@ -964,7 +977,7 @@ const PRIMES46: &'static [u64] = &[ 70368744176711, ]; -const PRIMES47: &'static [u64] = &[ +const PRIMES47: &[u64] = &[ 140737488355213, 140737488355201, 140737488355181, @@ -986,7 +999,7 @@ const PRIMES47: &'static [u64] = &[ 140737488354329, ]; -const PRIMES48: &'static [u64] = &[ +const PRIMES48: &[u64] = &[ 281474976710597, 281474976710591, 281474976710567, @@ -1019,7 +1032,7 @@ const PRIMES48: &'static [u64] = &[ 281474976709637, ]; -const PRIMES49: &'static [u64] = &[ +const PRIMES49: &[u64] = &[ 562949953421231, 562949953421201, 562949953421189, @@ -1053,7 +1066,7 @@ const PRIMES49: &'static [u64] = &[ 562949953420297, ]; -const PRIMES50: &'static [u64] = &[ +const PRIMES50: &[u64] = &[ 1125899906842597, 1125899906842589, 1125899906842573, diff --git a/tests/by-util/test_head.rs b/tests/by-util/test_head.rs index b2a3cf0cb..cf7c9c2ee 100755 --- a/tests/by-util/test_head.rs +++ b/tests/by-util/test_head.rs @@ -1,6 +1,6 @@ use crate::common::util::*; -static INPUT: &'static str = "lorem_ipsum.txt"; +static INPUT: &str = "lorem_ipsum.txt"; #[test] fn test_stdin_default() { diff --git a/tests/by-util/test_install.rs b/tests/by-util/test_install.rs index fb79454c1..6da357170 100644 --- a/tests/by-util/test_install.rs +++ b/tests/by-util/test_install.rs @@ -116,11 +116,11 @@ fn test_install_ancestors_mode_directories() { assert!(at.dir_exists(ancestor2)); assert!(at.dir_exists(target_dir)); - assert_ne!(0o40700 as u32, at.metadata(ancestor1).permissions().mode()); - assert_ne!(0o40700 as u32, at.metadata(ancestor2).permissions().mode()); + assert_ne!(0o40_700_u32, at.metadata(ancestor1).permissions().mode()); + assert_ne!(0o40_700_u32, at.metadata(ancestor2).permissions().mode()); // Expected mode only on the target_dir. - assert_eq!(0o40700 as u32, at.metadata(target_dir).permissions().mode()); + assert_eq!(0o40_700_u32, at.metadata(target_dir).permissions().mode()); } #[test] @@ -184,7 +184,7 @@ fn test_install_mode_numeric() { assert!(at.file_exists(file)); assert!(at.file_exists(dest_file)); let permissions = at.metadata(dest_file).permissions(); - assert_eq!(0o100333 as u32, PermissionsExt::mode(&permissions)); + assert_eq!(0o100_333_u32, PermissionsExt::mode(&permissions)); let mode_arg = "-m 0333"; at.mkdir(dir2); @@ -195,7 +195,7 @@ fn test_install_mode_numeric() { assert!(at.file_exists(file)); assert!(at.file_exists(dest_file)); let permissions = at.metadata(dest_file).permissions(); - assert_eq!(0o100333 as u32, PermissionsExt::mode(&permissions)); + assert_eq!(0o100_333_u32, PermissionsExt::mode(&permissions)); } #[test] @@ -213,7 +213,7 @@ fn test_install_mode_symbolic() { assert!(at.file_exists(file)); assert!(at.file_exists(dest_file)); let permissions = at.metadata(dest_file).permissions(); - assert_eq!(0o100003 as u32, PermissionsExt::mode(&permissions)); + assert_eq!(0o100_003_u32, PermissionsExt::mode(&permissions)); } #[test] @@ -251,7 +251,7 @@ fn test_install_mode_directories() { assert!(at.dir_exists(component)); let permissions = at.metadata(component).permissions(); - assert_eq!(0o040333 as u32, PermissionsExt::mode(&permissions)); + assert_eq!(0o040_333_u32, PermissionsExt::mode(&permissions)); } #[test] diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 2ae57ad7f..9614f561e 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -19,9 +19,7 @@ use std::path::PathBuf; #[cfg(not(windows))] use std::sync::Mutex; #[cfg(not(windows))] -extern crate tempdir; -#[cfg(not(windows))] -use self::tempdir::TempDir; +extern crate tempfile; #[cfg(not(windows))] lazy_static! { @@ -53,6 +51,7 @@ fn test_ls_a() { .unwrap(), ); + #[allow(clippy::trivial_regex)] let re_pwd = Regex::new(r"^\.\n").unwrap(); // Using the present working directory @@ -126,7 +125,7 @@ fn test_ls_width() { for option in &["-w 100", "-w=100", "--width=100", "--width 100"] { scene .ucmd() - .args(&option.split(" ").collect::>()) + .args(&option.split(' ').collect::>()) .succeeds() .stdout_only("test-width-1 test-width-2 test-width-3 test-width-4\n"); } @@ -134,7 +133,7 @@ fn test_ls_width() { for option in &["-w 50", "-w=50", "--width=50", "--width 50"] { scene .ucmd() - .args(&option.split(" ").collect::>()) + .args(&option.split(' ').collect::>()) .succeeds() .stdout_only("test-width-1 test-width-3\ntest-width-2 test-width-4\n"); } @@ -151,7 +150,7 @@ fn test_ls_width() { ] { scene .ucmd() - .args(&option.split(" ").collect::>()) + .args(&option.split(' ').collect::>()) .succeeds() .stdout_only("test-width-1\ntest-width-2\ntest-width-3\ntest-width-4\n"); } @@ -165,7 +164,7 @@ fn test_ls_width() { for option in &["-w 1a", "-w=1a", "--width=1a", "--width 1a"] { scene .ucmd() - .args(&option.split(" ").collect::>()) + .args(&option.split(' ').collect::>()) .fails() .stderr_only("ls: invalid line width: ‘1a’"); } @@ -419,7 +418,7 @@ fn test_ls_long_formats() { ] { let result = scene .ucmd() - .args(&arg.split(" ").collect::>()) + .args(&arg.split(' ').collect::>()) .arg("test-long-formats") .succeeds(); assert!(re_two.is_match(result.stdout_str())); @@ -429,7 +428,7 @@ fn test_ls_long_formats() { let result = scene .ucmd() .arg("-n") - .args(&arg.split(" ").collect::>()) + .args(&arg.split(' ').collect::>()) .arg("test-long-formats") .succeeds(); assert!(re_two_num.is_match(result.stdout_str())); @@ -448,7 +447,7 @@ fn test_ls_long_formats() { ] { let result = scene .ucmd() - .args(&arg.split(" ").collect::>()) + .args(&arg.split(' ').collect::>()) .arg("test-long-formats") .succeeds(); assert!(re_one.is_match(result.stdout_str())); @@ -458,7 +457,7 @@ fn test_ls_long_formats() { let result = scene .ucmd() .arg("-n") - .args(&arg.split(" ").collect::>()) + .args(&arg.split(' ').collect::>()) .arg("test-long-formats") .succeeds(); assert!(re_one_num.is_match(result.stdout_str())); @@ -480,7 +479,7 @@ fn test_ls_long_formats() { ] { let result = scene .ucmd() - .args(&arg.split(" ").collect::>()) + .args(&arg.split(' ').collect::>()) .arg("test-long-formats") .succeeds(); assert!(re_zero.is_match(result.stdout_str())); @@ -490,7 +489,7 @@ fn test_ls_long_formats() { let result = scene .ucmd() .arg("-n") - .args(&arg.split(" ").collect::>()) + .args(&arg.split(' ').collect::>()) .arg("test-long-formats") .succeeds(); assert!(re_zero.is_match(result.stdout_str())); @@ -900,6 +899,12 @@ fn test_ls_recursive() { scene.ucmd().arg("a").succeeds(); scene.ucmd().arg("a/a").succeeds(); + scene + .ucmd() + .arg("z") + .arg("-R") + .succeeds() + .stdout_contains(&"z:"); let result = scene .ucmd() .arg("--color=never") @@ -1065,7 +1070,7 @@ fn test_ls_indicator_style() { for opt in options { scene .ucmd() - .arg(format!("{}", opt)) + .arg(opt.to_string()) .succeeds() .stdout_contains(&"/"); } @@ -1087,7 +1092,10 @@ fn test_ls_indicator_style() { { use self::unix_socket::UnixListener; - let dir = TempDir::new("unix_socket").expect("failed to create dir"); + let dir = tempfile::Builder::new() + .prefix("unix_socket") + .tempdir() + .expect("failed to create dir"); let socket_path = dir.path().join("sock"); let _listener = UnixListener::bind(&socket_path).expect("failed to create socket"); diff --git a/tests/by-util/test_mkdir.rs b/tests/by-util/test_mkdir.rs index ef3226c41..54a6fe3c8 100644 --- a/tests/by-util/test_mkdir.rs +++ b/tests/by-util/test_mkdir.rs @@ -1,12 +1,12 @@ use crate::common::util::*; -static TEST_DIR1: &'static str = "mkdir_test1"; -static TEST_DIR2: &'static str = "mkdir_test2"; -static TEST_DIR3: &'static str = "mkdir_test3"; -static TEST_DIR4: &'static str = "mkdir_test4/mkdir_test4_1"; -static TEST_DIR5: &'static str = "mkdir_test5/mkdir_test5_1"; -static TEST_DIR6: &'static str = "mkdir_test6"; -static TEST_FILE7: &'static str = "mkdir_test7"; +static TEST_DIR1: &str = "mkdir_test1"; +static TEST_DIR2: &str = "mkdir_test2"; +static TEST_DIR3: &str = "mkdir_test3"; +static TEST_DIR4: &str = "mkdir_test4/mkdir_test4_1"; +static TEST_DIR5: &str = "mkdir_test5/mkdir_test5_1"; +static TEST_DIR6: &str = "mkdir_test6"; +static TEST_FILE7: &str = "mkdir_test7"; #[test] fn test_mkdir_mkdir() { diff --git a/tests/by-util/test_mktemp.rs b/tests/by-util/test_mktemp.rs index 617f0fd06..d0737764f 100644 --- a/tests/by-util/test_mktemp.rs +++ b/tests/by-util/test_mktemp.rs @@ -3,19 +3,19 @@ use crate::common::util::*; use std::path::PathBuf; use tempfile::tempdir; -static TEST_TEMPLATE1: &'static str = "tempXXXXXX"; -static TEST_TEMPLATE2: &'static str = "temp"; -static TEST_TEMPLATE3: &'static str = "tempX"; -static TEST_TEMPLATE4: &'static str = "tempXX"; -static TEST_TEMPLATE5: &'static str = "tempXXX"; -static TEST_TEMPLATE6: &'static str = "tempXXXlate"; -static TEST_TEMPLATE7: &'static str = "XXXtemplate"; +static TEST_TEMPLATE1: &str = "tempXXXXXX"; +static TEST_TEMPLATE2: &str = "temp"; +static TEST_TEMPLATE3: &str = "tempX"; +static TEST_TEMPLATE4: &str = "tempXX"; +static TEST_TEMPLATE5: &str = "tempXXX"; +static TEST_TEMPLATE6: &str = "tempXXXlate"; +static TEST_TEMPLATE7: &str = "XXXtemplate"; #[cfg(unix)] -static TEST_TEMPLATE8: &'static str = "tempXXXl/ate"; +static TEST_TEMPLATE8: &str = "tempXXXl/ate"; #[cfg(windows)] -static TEST_TEMPLATE8: &'static str = "tempXXXl\\ate"; +static TEST_TEMPLATE8: &str = "tempXXXl\\ate"; -const TMPDIR: &'static str = "TMPDIR"; +const TMPDIR: &str = "TMPDIR"; #[test] fn test_mktemp_mktemp() { diff --git a/tests/by-util/test_more.rs b/tests/by-util/test_more.rs index 9245733ca..cc778f0b4 100644 --- a/tests/by-util/test_more.rs +++ b/tests/by-util/test_more.rs @@ -2,15 +2,22 @@ use crate::common::util::*; #[test] fn test_more_no_arg() { - // stderr = more: Reading from stdin isn't supported yet. - new_ucmd!().fails(); + // Reading from stdin is now supported, so this must succeed + if atty::is(atty::Stream::Stdout) { + new_ucmd!().succeeds(); + } else {} } #[test] fn test_more_dir_arg() { - let result = new_ucmd!().arg(".").run(); - result.failure(); - const EXPECTED_ERROR_MESSAGE: &str = - "more: '.' is a directory.\nTry 'more --help' for more information."; - assert_eq!(result.stderr_str().trim(), EXPECTED_ERROR_MESSAGE); + // Run the test only if there's a valud terminal, else do nothing + // Maybe we could capture the error, i.e. "Device not found" in that case + // but I am leaving this for later + if atty::is(atty::Stream::Stdout) { + let result = new_ucmd!().arg(".").run(); + result.failure(); + const EXPECTED_ERROR_MESSAGE: &str = + "more: '.' is a directory.\nTry 'more --help' for more information."; + assert_eq!(result.stderr_str().trim(), EXPECTED_ERROR_MESSAGE); + } else {} } diff --git a/tests/by-util/test_mv.rs b/tests/by-util/test_mv.rs index e0bdd9ef3..beb8f61b9 100644 --- a/tests/by-util/test_mv.rs +++ b/tests/by-util/test_mv.rs @@ -82,7 +82,7 @@ fn test_mv_strip_slashes() { let dir = "test_mv_strip_slashes_dir"; let file = "test_mv_strip_slashes_file"; let mut source = file.to_owned(); - source.push_str("/"); + source.push('/'); at.mkdir(dir); at.touch(file); diff --git a/tests/by-util/test_od.rs b/tests/by-util/test_od.rs index b49e6f0ca..fa947aa6e 100644 --- a/tests/by-util/test_od.rs +++ b/tests/by-util/test_od.rs @@ -9,7 +9,7 @@ use std::io::Write; use std::path::Path; // octal dump of 'abcdefghijklmnopqrstuvwxyz\n' -static ALPHA_OUT: &'static str = " +static ALPHA_OUT: &str = " 0000000 061141 062143 063145 064147 065151 066153 067155 070157 0000020 071161 072163 073165 074167 075171 000012 0000033 @@ -563,7 +563,7 @@ fn test_dec_offset() { #[test] fn test_no_offset() { let input = [0u8; 31]; - const LINE: &'static str = " 00000000 00000000 00000000 00000000\n"; + const LINE: &str = " 00000000 00000000 00000000 00000000\n"; let expected_output = [LINE, LINE, LINE, LINE].join(""); new_ucmd!() diff --git a/tests/by-util/test_paste.rs b/tests/by-util/test_paste.rs index 4604c5cf5..1afe84be8 100644 --- a/tests/by-util/test_paste.rs +++ b/tests/by-util/test_paste.rs @@ -7,7 +7,7 @@ struct TestData<'b> { out: &'b str, } -static EXAMPLE_DATA: &'static [TestData<'static>] = &[ +static EXAMPLE_DATA: &[TestData] = &[ // Ensure that paste properly handles files lacking a final newline. TestData { name: "no-nl-1", @@ -64,8 +64,8 @@ static EXAMPLE_DATA: &'static [TestData<'static>] = &[ #[test] fn test_combine_pairs_of_lines() { - for s in vec!["-s", "--serial"] { - for d in vec!["-d", "--delimiters"] { + for &s in &["-s", "--serial"] { + for &d in &["-d", "--delimiters"] { new_ucmd!() .args(&[s, d, "\t\n", "html_colors.txt"]) .run() @@ -76,7 +76,7 @@ fn test_combine_pairs_of_lines() { #[test] fn test_multi_stdin() { - for d in vec!["-d", "--delimiters"] { + for &d in &["-d", "--delimiters"] { new_ucmd!() .args(&[d, "\t\n", "-", "-"]) .pipe_in_fixture("html_colors.txt") diff --git a/tests/by-util/test_readlink.rs b/tests/by-util/test_readlink.rs index cae5eafee..51aebbed2 100644 --- a/tests/by-util/test_readlink.rs +++ b/tests/by-util/test_readlink.rs @@ -1,6 +1,6 @@ use crate::common::util::*; -static GIBBERISH: &'static str = "supercalifragilisticexpialidocious"; +static GIBBERISH: &str = "supercalifragilisticexpialidocious"; #[test] fn test_canonicalize() { diff --git a/tests/by-util/test_relpath.rs b/tests/by-util/test_relpath.rs index 70d9f2a5d..b9c07fb12 100644 --- a/tests/by-util/test_relpath.rs +++ b/tests/by-util/test_relpath.rs @@ -61,6 +61,7 @@ const TESTS: [TestCase; 10] = [ }, ]; +#[allow(clippy::needless_lifetimes)] fn convert_path<'a>(path: &'a str) -> Cow<'a, str> { #[cfg(windows)] return path.replace("/", "\\").into(); diff --git a/tests/by-util/test_rmdir.rs b/tests/by-util/test_rmdir.rs index eef2d50f5..4b74b2522 100644 --- a/tests/by-util/test_rmdir.rs +++ b/tests/by-util/test_rmdir.rs @@ -108,3 +108,19 @@ fn test_rmdir_ignore_nonempty_directory_with_parents() { assert!(at.dir_exists(dir)); } + +#[test] +fn test_rmdir_remove_symlink_match_gnu_error() { + let (at, mut ucmd) = at_and_ucmd!(); + + let file = "file"; + let fl = "fl"; + at.touch(file); + assert!(at.file_exists(file)); + at.symlink_file(file, fl); + assert!(at.file_exists(fl)); + + ucmd.arg("fl/") + .fails() + .stderr_is("rmdir: failed to remove 'fl/': Not a directory"); +} diff --git a/tests/by-util/test_shuf.rs b/tests/by-util/test_shuf.rs index f925f8357..106d80a39 100644 --- a/tests/by-util/test_shuf.rs +++ b/tests/by-util/test_shuf.rs @@ -14,11 +14,11 @@ fn test_output_is_random_permutation() { let mut result_seq: Vec = result .stdout_str() - .split("\n") + .split('\n') .filter(|x| !x.is_empty()) .map(|x| x.parse().unwrap()) .collect(); - result_seq.sort(); + result_seq.sort_unstable(); assert_ne!(result.stdout_str(), input, "Output is not randomised"); assert_eq!(result_seq, input_seq, "Output is not a permutation"); } @@ -31,11 +31,11 @@ fn test_zero_termination() { let mut result_seq: Vec = result .stdout_str() - .split("\0") + .split('\0') .filter(|x| !x.is_empty()) .map(|x| x.parse().unwrap()) .collect(); - result_seq.sort(); + result_seq.sort_unstable(); assert_eq!(result_seq, input_seq, "Output is not a permutation"); } @@ -55,11 +55,11 @@ fn test_echo() { let mut result_seq: Vec = result .stdout_str() - .split("\n") + .split('\n') .filter(|x| !x.is_empty()) .map(|x| x.parse().unwrap()) .collect(); - result_seq.sort(); + result_seq.sort_unstable(); assert_eq!(result_seq, input_seq, "Output is not a permutation"); } @@ -81,11 +81,11 @@ fn test_head_count() { let mut result_seq: Vec = result .stdout_str() - .split("\n") + .split('\n') .filter(|x| !x.is_empty()) .map(|x| x.parse().unwrap()) .collect(); - result_seq.sort(); + result_seq.sort_unstable(); assert_eq!(result_seq.len(), repeat_limit, "Output is not limited"); assert!( result_seq.iter().all(|x| input_seq.contains(x)), @@ -113,7 +113,7 @@ fn test_repeat() { let result_seq: Vec = result .stdout_str() - .split("\n") + .split('\n') .filter(|x| !x.is_empty()) .map(|x| x.parse().unwrap()) .collect(); @@ -141,11 +141,11 @@ fn test_file_input() { let mut result_seq: Vec = result .stdout_str() - .split("\n") + .split('\n') .filter(|x| !x.is_empty()) .map(|x| x.parse().unwrap()) .collect(); - result_seq.sort(); + result_seq.sort_unstable(); assert_eq!(result_seq, expected_seq, "Output is not a permutation"); } diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 133dc0028..588ce86bd 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -4,14 +4,14 @@ fn test_helper(file_name: &str, possible_args: &[&str]) { for args in possible_args { new_ucmd!() .arg(format!("{}.txt", file_name)) - .args(&args.split(' ').collect::>()) + .args(&args.split_whitespace().collect::>()) .succeeds() .stdout_is_fixture(format!("{}.expected", file_name)); new_ucmd!() .arg(format!("{}.txt", file_name)) .arg("--debug") - .args(&args.split(' ').collect::>()) + .args(&args.split_whitespace().collect::>()) .succeeds() .stdout_is_fixture(format!("{}.expected.debug", file_name)); } @@ -288,7 +288,7 @@ fn test_dictionary_order() { #[test] fn test_dictionary_order2() { - for non_dictionary_order2_param in vec!["-d"] { + for non_dictionary_order2_param in &["-d"] { new_ucmd!() .pipe_in("a👦🏻aa b\naaaa b") .arg(non_dictionary_order2_param) @@ -299,7 +299,7 @@ fn test_dictionary_order2() { #[test] fn test_non_printing_chars() { - for non_printing_chars_param in vec!["-i"] { + for non_printing_chars_param in &["-i"] { new_ucmd!() .pipe_in("a👦🏻aa\naaaa") .arg(non_printing_chars_param) @@ -361,7 +361,7 @@ fn test_mixed_floats_ints_chars_numeric_stable() { #[test] fn test_numeric_floats_and_ints2() { - for numeric_sort_param in vec!["-n", "--numeric-sort"] { + for numeric_sort_param in &["-n", "--numeric-sort"] { let input = "1.444\n8.013\n1\n-8\n1.04\n-1"; new_ucmd!() .arg(numeric_sort_param) @@ -373,7 +373,7 @@ fn test_numeric_floats_and_ints2() { #[test] fn test_numeric_floats2() { - for numeric_sort_param in vec!["-n", "--numeric-sort"] { + for numeric_sort_param in &["-n", "--numeric-sort"] { let input = "1.444\n8.013\n1.58590\n-8.90880\n1.040000000\n-.05"; new_ucmd!() .arg(numeric_sort_param) @@ -426,7 +426,7 @@ fn test_default_unsorted_ints2() { #[test] fn test_numeric_unique_ints2() { - for numeric_unique_sort_param in vec!["-nu"] { + for numeric_unique_sort_param in &["-nu"] { let input = "9\n9\n8\n1\n"; new_ucmd!() .arg(numeric_unique_sort_param) @@ -471,7 +471,7 @@ fn test_keys_invalid_field() { new_ucmd!() .args(&["-k", "1."]) .fails() - .stderr_only("sort: failed to parse character index for key `1.`: cannot parse integer from empty string"); + .stderr_only("sort: failed to parse key `1.`: failed to parse character index ``: cannot parse integer from empty string"); } #[test] @@ -479,7 +479,7 @@ fn test_keys_invalid_field_option() { new_ucmd!() .args(&["-k", "1.1x"]) .fails() - .stderr_only("sort: invalid option for key: `x`"); + .stderr_only("sort: failed to parse key `1.1x`: invalid option: `x`"); } #[test] @@ -487,7 +487,7 @@ fn test_keys_invalid_field_zero() { new_ucmd!() .args(&["-k", "0.1"]) .fails() - .stderr_only("sort: field index was 0"); + .stderr_only("sort: failed to parse key `0.1`: field index can not be 0"); } #[test] @@ -495,7 +495,7 @@ fn test_keys_invalid_char_zero() { new_ucmd!() .args(&["-k", "1.0"]) .fails() - .stderr_only("sort: invalid character index 0 in `1.0` for the start position of a field"); + .stderr_only("sort: failed to parse key `1.0`: invalid character index 0 for the start position of a field"); } #[test] @@ -526,6 +526,11 @@ fn test_keys_with_options_blanks_start() { } } +#[test] +fn test_keys_blanks_with_char_idx() { + test_helper("keys_blanks", &["-k 1.2b"]) +} + #[test] fn test_keys_with_options_blanks_end() { let input = "a b @@ -574,6 +579,54 @@ aaaa .stdout_only(input); } +#[test] +fn test_keys_negative_size_match() { + // If the end of a field is before its start, we should not crash. + // Debug output should report "no match for key" at the start position (i.e. the later position). + test_helper("keys_negative_size", &["-k 3,1"]); +} + +#[test] +fn test_keys_ignore_flag() { + test_helper("keys_ignore_flag", &["-k 1n -b"]) +} + +#[test] +fn test_doesnt_inherit_key_settings() { + let input = " 1 +2 + 10 +"; + new_ucmd!() + .args(&["-k", "1b", "-n"]) + .pipe_in(input) + .succeeds() + .stdout_only( + " 1 + 10 +2 +", + ); +} + +#[test] +fn test_inherits_key_settings() { + let input = " 1 +2 + 10 +"; + new_ucmd!() + .args(&["-k", "1", "-n"]) + .pipe_in(input) + .succeeds() + .stdout_only( + " 1 +2 + 10 +", + ); +} + #[test] fn test_zero_terminated() { test_helper("zero-terminated", &["-z"]); @@ -695,10 +748,9 @@ fn test_dictionary_and_nonprinting_conflicts() { .succeeds(); } for conflicting_arg in &conflicting_args { - // FIXME: this should ideally fail. new_ucmd!() .args(&["-k", &format!("1{},1{}", restricted_arg, conflicting_arg)]) - .succeeds(); + .fails(); } } } @@ -711,3 +763,22 @@ fn test_trailing_separator() { .succeeds() .stdout_is("aax\naaa\n"); } + +#[test] +fn test_nonexistent_file() { + new_ucmd!() + .arg("nonexistent.txt") + .fails() + .status_code(2) + .stderr_only( + #[cfg(not(windows))] + "sort: cannot read: \"nonexistent.txt\": No such file or directory (os error 2)", + #[cfg(windows)] + "sort: cannot read: \"nonexistent.txt\": The system cannot find the file specified. (os error 2)", + ); +} + +#[test] +fn test_blanks() { + test_helper("blanks", &["-b", "--ignore-blanks"]); +} diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index d83de4323..1ff8bd8f2 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -98,7 +98,7 @@ impl RandomFile { let to_write = std::cmp::min(remaining_size, buffer.len()); let buf = &mut buffer[..to_write]; rng.fill(buf); - writer.write(buf).unwrap(); + writer.write_all(buf).unwrap(); remaining_size -= to_write; } @@ -179,6 +179,7 @@ fn test_split_bytes_prime_part_size() { let mut fns = glob.collect(); // glob.collect() is not guaranteed to return in sorted order, so we sort. fns.sort(); + #[allow(clippy::needless_range_loop)] for i in 0..5 { assert_eq!(glob.directory.metadata(&fns[i]).len(), 1753); } @@ -246,9 +247,9 @@ fn test_filter() { assert!( glob.collate().iter().find(|&&c| { // is not i - c != ('i' as u8) + c != (b'i') // is not newline - && c != ('\n' as u8) + && c != (b'\n') }) == None ); } @@ -271,7 +272,7 @@ fn test_filter_with_env_var_set() { let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$"); assert_eq!(glob.collate(), at.read_bytes(name)); - assert!(env::var("FILE").unwrap_or("var was unset".to_owned()) == env_var_value); + assert!(env::var("FILE").unwrap_or_else(|_| "var was unset".to_owned()) == env_var_value); } #[test] diff --git a/tests/by-util/test_stat.rs b/tests/by-util/test_stat.rs index 44bce9cd8..6935cc7f9 100644 --- a/tests/by-util/test_stat.rs +++ b/tests/by-util/test_stat.rs @@ -97,13 +97,13 @@ fn test_invalid_option() { } #[cfg(any(target_os = "linux", target_vendor = "apple"))] -const NORMAL_FMTSTR: &'static str = +const NORMAL_FMTSTR: &str = "%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s %u %U %x %X %y %Y %z %Z"; // avoid "%w %W" (birth/creation) due to `stat` limitations and linux kernel & rust version capability variations #[cfg(any(target_os = "linux"))] -const DEV_FMTSTR: &'static str = +const DEV_FMTSTR: &str = "%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s (%t/%T) %u %U %w %W %x %X %y %Y %z %Z"; #[cfg(target_os = "linux")] -const FS_FMTSTR: &'static str = "%b %c %i %l %n %s %S %t %T"; // avoid "%a %d %f" which can cause test failure due to race conditions +const FS_FMTSTR: &str = "%b %c %i %l %n %s %S %t %T"; // avoid "%a %d %f" which can cause test failure due to race conditions #[test] #[cfg(target_os = "linux")] @@ -140,7 +140,7 @@ fn test_terse_normal_format() { assert!(!v_expect.is_empty()); // uu_stat does not support selinux - if v_actual.len() == v_expect.len() - 1 && v_expect[v_expect.len() - 1].contains(":") { + if v_actual.len() == v_expect.len() - 1 && v_expect[v_expect.len() - 1].contains(':') { // assume last element contains: `SELinux security context string` v_expect.pop(); } @@ -222,7 +222,7 @@ fn test_symlinks() { let mut tested: bool = false; // arbitrarily chosen symlinks with hope that the CI environment provides at least one of them - for file in vec![ + for file in &[ "/bin/sh", "/bin/sudoedit", "/usr/bin/ex", diff --git a/tests/by-util/test_tail.rs b/tests/by-util/test_tail.rs index 27e94a78f..6227ac60b 100644 --- a/tests/by-util/test_tail.rs +++ b/tests/by-util/test_tail.rs @@ -4,9 +4,9 @@ use crate::common::util::*; use std::char::from_digit; use std::io::Write; -static FOOBAR_TXT: &'static str = "foobar.txt"; -static FOOBAR_2_TXT: &'static str = "foobar2.txt"; -static FOOBAR_WITH_NULL_TXT: &'static str = "foobar_with_null.txt"; +static FOOBAR_TXT: &str = "foobar.txt"; +static FOOBAR_2_TXT: &str = "foobar2.txt"; +static FOOBAR_WITH_NULL_TXT: &str = "foobar_with_null.txt"; #[test] fn test_stdin_default() { @@ -152,8 +152,8 @@ fn test_follow_with_pid() { #[test] fn test_single_big_args() { - const FILE: &'static str = "single_big_args.txt"; - const EXPECTED_FILE: &'static str = "single_big_args_expected.txt"; + const FILE: &str = "single_big_args.txt"; + const EXPECTED_FILE: &str = "single_big_args_expected.txt"; const LINES: usize = 1_000_000; const N_ARG: usize = 100_000; @@ -161,13 +161,13 @@ fn test_single_big_args() { let mut big_input = at.make_file(FILE); for i in 0..LINES { - write!(&mut big_input, "Line {}\n", i).expect("Could not write to FILE"); + writeln!(&mut big_input, "Line {}", i).expect("Could not write to FILE"); } big_input.flush().expect("Could not flush FILE"); let mut big_expected = at.make_file(EXPECTED_FILE); for i in (LINES - N_ARG)..LINES { - write!(&mut big_expected, "Line {}\n", i).expect("Could not write to EXPECTED_FILE"); + writeln!(&mut big_expected, "Line {}", i).expect("Could not write to EXPECTED_FILE"); } big_expected.flush().expect("Could not flush EXPECTED_FILE"); @@ -200,8 +200,8 @@ fn test_bytes_stdin() { #[test] fn test_bytes_big() { - const FILE: &'static str = "test_bytes_big.txt"; - const EXPECTED_FILE: &'static str = "test_bytes_big_expected.txt"; + const FILE: &str = "test_bytes_big.txt"; + const EXPECTED_FILE: &str = "test_bytes_big_expected.txt"; const BYTES: usize = 1_000_000; const N_ARG: usize = 100_000; @@ -237,8 +237,8 @@ fn test_bytes_big() { #[test] fn test_lines_with_size_suffix() { - const FILE: &'static str = "test_lines_with_size_suffix.txt"; - const EXPECTED_FILE: &'static str = "test_lines_with_size_suffix_expected.txt"; + const FILE: &str = "test_lines_with_size_suffix.txt"; + const EXPECTED_FILE: &str = "test_lines_with_size_suffix_expected.txt"; const LINES: usize = 3_000; const N_ARG: usize = 2 * 1024; diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index 40fbb8aa9..d4d2c058e 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -401,8 +401,8 @@ fn get_dstswitch_hour() -> Option { for _i in 0..(366 * 24) { if is_dst_switch_hour(ts) { let mut tm = time::at(ts); - tm.tm_hour = tm.tm_hour + 1; - let s = time::strftime("%Y%m%d%H%M", &tm).unwrap().to_string(); + tm.tm_hour += 1; + let s = time::strftime("%Y%m%d%H%M", &tm).unwrap(); return Some(s); } ts = ts + time::Duration::hours(1); @@ -415,10 +415,7 @@ fn test_touch_mtime_dst_fails() { let (_at, mut ucmd) = at_and_ucmd!(); let file = "test_touch_set_mtime_dst_fails"; - match get_dstswitch_hour() { - Some(s) => { - ucmd.args(&["-m", "-t", &s, file]).fails(); - } - None => (), + if let Some(s) = get_dstswitch_hour() { + ucmd.args(&["-m", "-t", &s, file]).fails(); } } diff --git a/tests/by-util/test_truncate.rs b/tests/by-util/test_truncate.rs index 120982e3c..04fc826a5 100644 --- a/tests/by-util/test_truncate.rs +++ b/tests/by-util/test_truncate.rs @@ -1,8 +1,8 @@ use crate::common::util::*; use std::io::{Seek, SeekFrom, Write}; -static TFILE1: &'static str = "truncate_test_1"; -static TFILE2: &'static str = "truncate_test_2"; +static TFILE1: &str = "truncate_test_1"; +static TFILE2: &str = "truncate_test_2"; #[test] fn test_increase_file_size() { @@ -45,14 +45,17 @@ fn test_reference() { let at = &scene.fixtures; let mut file = at.make_file(TFILE2); - scene.ucmd().arg("-s").arg("+5KB").arg(TFILE1).run(); + // TODO: 'truncate' should create the file in this case because '--no-create' wasn't used + // A FILE argument that does not exist is created. + at.touch(TFILE1); + scene.ucmd().arg("-s").arg("+5KB").arg(TFILE1).succeeds(); scene .ucmd() .arg("--reference") .arg(TFILE1) .arg(TFILE2) - .run(); + .succeeds(); file.seek(SeekFrom::End(0)).unwrap(); let actual = file.seek(SeekFrom::Current(0)).unwrap(); @@ -262,3 +265,11 @@ fn test_reference_file_not_found() { .fails() .stderr_contains("cannot stat 'a': No such file or directory"); } + +#[test] +fn test_reference_with_size_file_not_found() { + new_ucmd!() + .args(&["-r", "a", "-s", "+1", "b"]) + .fails() + .stderr_contains("cannot stat 'a': No such file or directory"); +} diff --git a/tests/by-util/test_uniq.rs b/tests/by-util/test_uniq.rs index 2645c38ca..c191ffcaf 100644 --- a/tests/by-util/test_uniq.rs +++ b/tests/by-util/test_uniq.rs @@ -1,10 +1,10 @@ use crate::common::util::*; -static INPUT: &'static str = "sorted.txt"; -static OUTPUT: &'static str = "sorted-output.txt"; -static SKIP_CHARS: &'static str = "skip-chars.txt"; -static SKIP_FIELDS: &'static str = "skip-fields.txt"; -static SORTED_ZERO_TERMINATED: &'static str = "sorted-zero-terminated.txt"; +static INPUT: &str = "sorted.txt"; +static OUTPUT: &str = "sorted-output.txt"; +static SKIP_CHARS: &str = "skip-chars.txt"; +static SKIP_FIELDS: &str = "skip-fields.txt"; +static SORTED_ZERO_TERMINATED: &str = "sorted-zero-terminated.txt"; #[test] fn test_stdin_default() { diff --git a/tests/by-util/test_users.rs b/tests/by-util/test_users.rs index 3c5789820..89c3fdd0f 100644 --- a/tests/by-util/test_users.rs +++ b/tests/by-util/test_users.rs @@ -1,27 +1,23 @@ use crate::common::util::*; -use std::env; #[test] fn test_users_noarg() { new_ucmd!().succeeds(); } + #[test] +#[cfg(any(target_vendor = "apple", target_os = "linux"))] fn test_users_check_name() { - let result = TestScenario::new(util_name!()).ucmd_keepenv().succeeds(); + #[cfg(target_os = "linux")] + let util_name = util_name!(); + #[cfg(target_vendor = "apple")] + let util_name = format!("g{}", util_name!()); - // Expectation: USER is often set - let key = "USER"; + let expected = TestScenario::new(&util_name) + .cmd_keepenv(util_name) + .env("LANGUAGE", "C") + .succeeds() + .stdout_move_str(); - match env::var(key) { - Err(e) => println!("Key {} isn't set. Found {}", &key, e), - Ok(username) => - // Check if "users" contains the name of the user - { - println!("username found {}", &username); - // println!("result.stdout {}", &result.stdout); - if !result.stdout_str().is_empty() { - result.stdout_contains(&username); - } - } - } + new_ucmd!().succeeds().stdout_is(&expected); } diff --git a/tests/by-util/test_who.rs b/tests/by-util/test_who.rs index 21b5eb93e..333b03f5b 100644 --- a/tests/by-util/test_who.rs +++ b/tests/by-util/test_who.rs @@ -3,7 +3,7 @@ use crate::common::util::*; #[cfg(any(target_vendor = "apple", target_os = "linux"))] #[test] fn test_count() { - for opt in vec!["-q", "--count"] { + for opt in &["-q", "--count"] { new_ucmd!() .arg(opt) .succeeds() @@ -14,7 +14,7 @@ fn test_count() { #[cfg(any(target_vendor = "apple", target_os = "linux"))] #[test] fn test_boot() { - for opt in vec!["-b", "--boot"] { + for opt in &["-b", "--boot"] { new_ucmd!() .arg(opt) .succeeds() @@ -25,7 +25,7 @@ fn test_boot() { #[cfg(any(target_vendor = "apple", target_os = "linux"))] #[test] fn test_heading() { - for opt in vec!["-H", "--heading"] { + for opt in &["-H", "--heading"] { // allow whitespace variation // * minor whitespace differences occur between platform built-in outputs; // specifically number of TABs between "TIME" and "COMMENT" may be variant @@ -42,7 +42,7 @@ fn test_heading() { #[cfg(any(target_vendor = "apple", target_os = "linux"))] #[test] fn test_short() { - for opt in vec!["-s", "--short"] { + for opt in &["-s", "--short"] { new_ucmd!() .arg(opt) .succeeds() @@ -53,7 +53,7 @@ fn test_short() { #[cfg(any(target_vendor = "apple", target_os = "linux"))] #[test] fn test_login() { - for opt in vec!["-l", "--login"] { + for opt in &["-l", "--login"] { new_ucmd!() .arg(opt) .succeeds() @@ -64,7 +64,7 @@ fn test_login() { #[cfg(any(target_vendor = "apple", target_os = "linux"))] #[test] fn test_m() { - for opt in vec!["-m"] { + for opt in &["-m"] { new_ucmd!() .arg(opt) .succeeds() @@ -75,7 +75,7 @@ fn test_m() { #[cfg(any(target_vendor = "apple", target_os = "linux"))] #[test] fn test_process() { - for opt in vec!["-p", "--process"] { + for opt in &["-p", "--process"] { new_ucmd!() .arg(opt) .succeeds() @@ -85,7 +85,7 @@ fn test_process() { #[test] fn test_runlevel() { - for opt in vec!["-r", "--runlevel"] { + for opt in &["-r", "--runlevel"] { #[cfg(any(target_vendor = "apple", target_os = "linux"))] new_ucmd!() .arg(opt) @@ -100,7 +100,7 @@ fn test_runlevel() { #[cfg(any(target_vendor = "apple", target_os = "linux"))] #[test] fn test_time() { - for opt in vec!["-t", "--time"] { + for opt in &["-t", "--time"] { new_ucmd!() .arg(opt) .succeeds() @@ -117,7 +117,7 @@ fn test_mesg() { // same as -T // --writable // same as -T - for opt in vec!["-T", "-w", "--mesg", "--message", "--writable"] { + for opt in &["-T", "-w", "--mesg", "--message", "--writable"] { new_ucmd!() .arg(opt) .succeeds() @@ -147,7 +147,7 @@ fn test_too_many_args() { #[cfg(any(target_vendor = "apple", target_os = "linux"))] #[test] fn test_users() { - for opt in vec!["-u", "--users"] { + for opt in &["-u", "--users"] { let actual = new_ucmd!().arg(opt).succeeds().stdout_move_str(); let expect = expected_result(&[opt]); println!("actual: {:?}", actual); @@ -172,18 +172,17 @@ fn test_users() { #[cfg(any(target_vendor = "apple", target_os = "linux"))] #[test] fn test_lookup() { - for opt in vec!["--lookup"] { - new_ucmd!() - .arg(opt) - .succeeds() - .stdout_is(expected_result(&[opt])); - } + let opt = "--lookup"; + new_ucmd!() + .arg(opt) + .succeeds() + .stdout_is(expected_result(&[opt])); } #[cfg(any(target_vendor = "apple", target_os = "linux"))] #[test] fn test_dead() { - for opt in vec!["-d", "--dead"] { + for opt in &["-d", "--dead"] { new_ucmd!() .arg(opt) .succeeds() @@ -222,7 +221,7 @@ fn test_all() { return; } - for opt in vec!["-a", "--all"] { + for opt in &["-a", "--all"] { new_ucmd!() .arg(opt) .succeeds() diff --git a/tests/common/util.rs b/tests/common/util.rs index 6f9f779ef..9ce7f0537 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -1,7 +1,4 @@ #![allow(dead_code)] - -#[cfg(not(windows))] -use libc; use pretty_assertions::assert_eq; use std::env; #[cfg(not(windows))] @@ -16,7 +13,6 @@ use std::os::windows::fs::{symlink_dir, symlink_file}; use std::path::{Path, PathBuf}; use std::process::{Child, Command, Stdio}; use std::rc::Rc; -use std::str::from_utf8; use std::thread::sleep; use std::time::Duration; use tempfile::TempDir; @@ -40,7 +36,7 @@ static NO_STDIN_MEANINGLESS: &str = "Setting this flag has no effect if there is /// Test if the program is running under CI pub fn is_ci() -> bool { std::env::var("CI") - .unwrap_or(String::from("false")) + .unwrap_or_else(|_| String::from("false")) .eq_ignore_ascii_case("true") } @@ -62,54 +58,54 @@ pub struct CmdResult { /// see [`success`] success: bool, /// captured standard output after running the Command - stdout: String, + stdout: Vec, /// captured standard error after running the Command - stderr: String, + stderr: Vec, } impl CmdResult { /// Returns a reference to the program's standard output as a slice of bytes pub fn stdout(&self) -> &[u8] { - &self.stdout.as_bytes() + &self.stdout } /// Returns the program's standard output as a string slice pub fn stdout_str(&self) -> &str { - &self.stdout + std::str::from_utf8(&self.stdout).unwrap() } /// Returns the program's standard output as a string /// consumes self pub fn stdout_move_str(self) -> String { - self.stdout + String::from_utf8(self.stdout).unwrap() } /// Returns the program's standard output as a vec of bytes /// consumes self pub fn stdout_move_bytes(self) -> Vec { - Vec::from(self.stdout) + self.stdout } /// Returns a reference to the program's standard error as a slice of bytes pub fn stderr(&self) -> &[u8] { - &self.stderr.as_bytes() + &self.stderr } /// Returns the program's standard error as a string slice pub fn stderr_str(&self) -> &str { - &self.stderr + std::str::from_utf8(&self.stderr).unwrap() } /// Returns the program's standard error as a string /// consumes self pub fn stderr_move_str(self) -> String { - self.stderr + String::from_utf8(self.stderr).unwrap() } /// Returns the program's standard error as a vec of bytes /// consumes self pub fn stderr_move_bytes(self) -> Vec { - Vec::from(self.stderr) + self.stderr } /// Returns the program's exit code @@ -202,21 +198,21 @@ impl CmdResult { /// passed in value, trailing whitespace are kept to force strict comparison (#1235) /// stdout_only is a better choice unless stderr may or will be non-empty pub fn stdout_is>(&self, msg: T) -> &CmdResult { - assert_eq!(self.stdout, String::from(msg.as_ref())); + assert_eq!(self.stdout_str(), String::from(msg.as_ref())); self } /// Like `stdout_is` but newlines are normalized to `\n`. pub fn normalized_newlines_stdout_is>(&self, msg: T) -> &CmdResult { let msg = msg.as_ref().replace("\r\n", "\n"); - assert_eq!(self.stdout.replace("\r\n", "\n"), msg); + assert_eq!(self.stdout_str().replace("\r\n", "\n"), msg); self } /// asserts that the command resulted in stdout stream output, /// whose bytes equal those of the passed in slice pub fn stdout_is_bytes>(&self, msg: T) -> &CmdResult { - assert_eq!(self.stdout.as_bytes(), msg.as_ref()); + assert_eq!(self.stdout, msg.as_ref()); self } @@ -231,7 +227,7 @@ impl CmdResult { /// stderr_only is a better choice unless stdout may or will be non-empty pub fn stderr_is>(&self, msg: T) -> &CmdResult { assert_eq!( - self.stderr.trim_end(), + self.stderr_str().trim_end(), String::from(msg.as_ref()).trim_end() ); self @@ -240,7 +236,7 @@ impl CmdResult { /// asserts that the command resulted in stderr stream output, /// whose bytes equal those of the passed in slice pub fn stderr_is_bytes>(&self, msg: T) -> &CmdResult { - assert_eq!(self.stderr.as_bytes(), msg.as_ref()); + assert_eq!(self.stderr, msg.as_ref()); self } @@ -465,7 +461,7 @@ impl AtPath { .append(true) .open(self.plus(name)) .unwrap(); - f.write(contents.as_bytes()) + f.write_all(contents.as_bytes()) .unwrap_or_else(|e| panic!("Couldn't write {}: {}", name, e)); } @@ -778,7 +774,7 @@ impl UCommand { if self.has_run { panic!("{}", ALREADY_RUN); } - self.comm_string.push_str(" "); + self.comm_string.push(' '); self.comm_string .push_str(arg.as_ref().to_str().unwrap_or_default()); self.raw.arg(arg.as_ref()); @@ -798,7 +794,7 @@ impl UCommand { .accept_any(); for s in strings { - self.comm_string.push_str(" "); + self.comm_string.push(' '); self.comm_string.push_str(&s); } @@ -854,9 +850,9 @@ impl UCommand { log_info("run", &self.comm_string); let mut child = self .raw - .stdin(self.stdin.take().unwrap_or_else(|| Stdio::piped())) - .stdout(self.stdout.take().unwrap_or_else(|| Stdio::piped())) - .stderr(self.stderr.take().unwrap_or_else(|| Stdio::piped())) + .stdin(self.stdin.take().unwrap_or_else(Stdio::piped)) + .stdout(self.stdout.take().unwrap_or_else(Stdio::piped)) + .stderr(self.stderr.take().unwrap_or_else(Stdio::piped)) .spawn() .unwrap(); @@ -886,8 +882,8 @@ impl UCommand { tmpd: self.tmpd.clone(), code: prog.status.code(), success: prog.status.success(), - stdout: from_utf8(&prog.stdout).unwrap().to_string(), - stderr: from_utf8(&prog.stderr).unwrap().to_string(), + stdout: prog.stdout, + stderr: prog.stderr, } } @@ -930,10 +926,7 @@ pub fn read_size(child: &mut Child, size: usize) -> String { } pub fn vec_of_size(n: usize) -> Vec { - let mut result = Vec::new(); - for _ in 0..n { - result.push('a' as u8); - } + let result = vec![b'a'; n]; assert_eq!(result.len(), n); result } diff --git a/tests/fixtures/sort/blanks.expected b/tests/fixtures/sort/blanks.expected new file mode 100644 index 000000000..3469e434c --- /dev/null +++ b/tests/fixtures/sort/blanks.expected @@ -0,0 +1,5 @@ + a +b + x + x + z diff --git a/tests/fixtures/sort/blanks.expected.debug b/tests/fixtures/sort/blanks.expected.debug new file mode 100644 index 000000000..28c8fc960 --- /dev/null +++ b/tests/fixtures/sort/blanks.expected.debug @@ -0,0 +1,15 @@ + a + _ +___ +b +_ +_ + x + _ +__________ + x + _ +___ + z + _ +__ diff --git a/tests/fixtures/sort/blanks.txt b/tests/fixtures/sort/blanks.txt new file mode 100644 index 000000000..eb2f3c94e --- /dev/null +++ b/tests/fixtures/sort/blanks.txt @@ -0,0 +1,5 @@ +b + a + z + x + x diff --git a/tests/fixtures/sort/exponents_general.expected b/tests/fixtures/sort/exponents_general.expected index 524b6e67f..308a82e1e 100644 --- a/tests/fixtures/sort/exponents_general.expected +++ b/tests/fixtures/sort/exponents_general.expected @@ -6,8 +6,15 @@ + -12e-5555.5 +0b10 // binary not supported +0x10 // hexadecimal not supported, but it should be +55e-20 +55e-20.10 5.5.5.5 10E +64e+ +99e- 1000EDKLD 10000K78 +100000 @@ -15,5 +22,7 @@ 100E6 100E6 10e10e10e10 +13e+10 +45e+10.5 50e10 50e10 diff --git a/tests/fixtures/sort/exponents_general.expected.debug b/tests/fixtures/sort/exponents_general.expected.debug index 4dea45c39..a7238d10e 100644 --- a/tests/fixtures/sort/exponents_general.expected.debug +++ b/tests/fixtures/sort/exponents_general.expected.debug @@ -22,12 +22,33 @@ ^ no match for key ^ no match for key + > -12e-5555.5 + _________ +______________ +0b10 // binary not supported +_ +____________________________ +0x10 // hexadecimal not supported, but it should be +_ +___________________________________________________ +55e-20 +______ +______ +55e-20.10 +______ +_________ 5.5.5.5 ___ _______ 10E __ ___ +64e+ +__ +____ +99e- +__ +____ 1000EDKLD ____ _________ @@ -49,6 +70,12 @@ _____ 10e10e10e10 _____ ___________ +13e+10 +______ +______ +45e+10.5 +______ +________ 50e10 _____ _____ diff --git a/tests/fixtures/sort/exponents_general.txt b/tests/fixtures/sort/exponents_general.txt index de2a6c31b..4a9bbba2e 100644 --- a/tests/fixtures/sort/exponents_general.txt +++ b/tests/fixtures/sort/exponents_general.txt @@ -4,16 +4,25 @@ +100000 10000K78 +0x10 // hexadecimal not supported, but it should be 10E +0b10 // binary not supported +64e+ +99e- +45e+10.5 1000EDKLD +13e+10 100E6 50e10 + -12e-5555.5 +100000 10e10e10e10 5.5.5.5 +55e-20 +55e-20.10 diff --git a/tests/fixtures/sort/keys_blanks.expected b/tests/fixtures/sort/keys_blanks.expected new file mode 100644 index 000000000..1789659c4 --- /dev/null +++ b/tests/fixtures/sort/keys_blanks.expected @@ -0,0 +1,3 @@ + cab + abc + bca diff --git a/tests/fixtures/sort/keys_blanks.expected.debug b/tests/fixtures/sort/keys_blanks.expected.debug new file mode 100644 index 000000000..bb09ea8a2 --- /dev/null +++ b/tests/fixtures/sort/keys_blanks.expected.debug @@ -0,0 +1,9 @@ +>cab + __ +____ +>abc + __ +____ +>bca + __ +____ diff --git a/tests/fixtures/sort/keys_blanks.txt b/tests/fixtures/sort/keys_blanks.txt new file mode 100644 index 000000000..7c4f313a3 --- /dev/null +++ b/tests/fixtures/sort/keys_blanks.txt @@ -0,0 +1,3 @@ + abc + cab + bca diff --git a/tests/fixtures/sort/keys_ignore_flag.expected b/tests/fixtures/sort/keys_ignore_flag.expected new file mode 100644 index 000000000..964d04663 --- /dev/null +++ b/tests/fixtures/sort/keys_ignore_flag.expected @@ -0,0 +1,2 @@ + 1a +1A diff --git a/tests/fixtures/sort/keys_ignore_flag.expected.debug b/tests/fixtures/sort/keys_ignore_flag.expected.debug new file mode 100644 index 000000000..6d0da711f --- /dev/null +++ b/tests/fixtures/sort/keys_ignore_flag.expected.debug @@ -0,0 +1,6 @@ + 1a + _ +___ +1A +_ +__ diff --git a/tests/fixtures/sort/keys_ignore_flag.txt b/tests/fixtures/sort/keys_ignore_flag.txt new file mode 100644 index 000000000..964d04663 --- /dev/null +++ b/tests/fixtures/sort/keys_ignore_flag.txt @@ -0,0 +1,2 @@ + 1a +1A diff --git a/tests/fixtures/sort/keys_negative_size.expected b/tests/fixtures/sort/keys_negative_size.expected new file mode 100644 index 000000000..3774da60e --- /dev/null +++ b/tests/fixtures/sort/keys_negative_size.expected @@ -0,0 +1 @@ +a b c diff --git a/tests/fixtures/sort/keys_negative_size.expected.debug b/tests/fixtures/sort/keys_negative_size.expected.debug new file mode 100644 index 000000000..e392ec7f3 --- /dev/null +++ b/tests/fixtures/sort/keys_negative_size.expected.debug @@ -0,0 +1,3 @@ +a b c + ^ no match for key +_____ diff --git a/tests/fixtures/sort/keys_negative_size.txt b/tests/fixtures/sort/keys_negative_size.txt new file mode 100644 index 000000000..3774da60e --- /dev/null +++ b/tests/fixtures/sort/keys_negative_size.txt @@ -0,0 +1 @@ +a b c diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric.expected b/tests/fixtures/sort/mixed_floats_ints_chars_numeric.expected index a781a36bb..59541af32 100644 --- a/tests/fixtures/sort/mixed_floats_ints_chars_numeric.expected +++ b/tests/fixtures/sort/mixed_floats_ints_chars_numeric.expected @@ -21,10 +21,10 @@ CARAvan 8.013 45 46.89 - 4567. - 37800 576,446.88800000 576,446.890 + 4567. + 37800 4798908.340000000000 4798908.45 4798908.8909800 diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric.expected.debug b/tests/fixtures/sort/mixed_floats_ints_chars_numeric.expected.debug index dbe295a1c..b7b76e589 100644 --- a/tests/fixtures/sort/mixed_floats_ints_chars_numeric.expected.debug +++ b/tests/fixtures/sort/mixed_floats_ints_chars_numeric.expected.debug @@ -67,18 +67,18 @@ __ 46.89 _____ _____ +576,446.88800000 +___ +________________ +576,446.890 +___ +___________ 4567. _____ ____________________ >>>>37800 _____ _________ -576,446.88800000 -________________ -________________ -576,446.890 -___________ -___________ 4798908.340000000000 ____________________ ____________________ diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_reverse.expected b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_reverse.expected deleted file mode 100644 index 6b024210b..000000000 --- a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_reverse.expected +++ /dev/null @@ -1,30 +0,0 @@ -4798908.8909800 -4798908.45 -4798908.340000000000 -576,446.890 -576,446.88800000 - 37800 - 4567. -46.89 -45 -8.013 -1.58590 -1.444 -1.040000000 -1 -00000001 -CARAvan -000 - - - - - - - - --.05 --1 --8.90880 --896689 --2028789030 diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_reverse_stable.expected b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_reverse_stable.expected deleted file mode 100644 index cb1028f0e..000000000 --- a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_reverse_stable.expected +++ /dev/null @@ -1,30 +0,0 @@ -4798908.8909800 -4798908.45 -4798908.340000000000 -576,446.890 -576,446.88800000 - 37800 - 4567. -46.89 -45 -8.013 -1.58590 -1.444 -1.040000000 -1 -00000001 - - - - - -CARAvan - - - -000 --.05 --1 --8.90880 --896689 --2028789030 diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_reverse_stable.txt b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_reverse_stable.txt deleted file mode 100644 index a5813ea3a..000000000 --- a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_reverse_stable.txt +++ /dev/null @@ -1,30 +0,0 @@ -576,446.890 -576,446.88800000 - - - 4567. -45 -46.89 --1 -1 -00000001 -4798908.340000000000 -4798908.45 -4798908.8909800 - - - 37800 - --2028789030 --896689 -CARAvan - --8.90880 --.05 -1.444 -1.58590 -1.040000000 - -8.013 - -000 diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_stable.expected b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_stable.expected index 63a3e646d..0ccdd84c0 100644 --- a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_stable.expected +++ b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_stable.expected @@ -8,11 +8,14 @@ +-0 CARAvan +-0 000 +-0 1 00000001 1.040000000 @@ -21,10 +24,10 @@ CARAvan 8.013 45 46.89 +576,446.890 +576,446.88800000 4567. 37800 -576,446.88800000 -576,446.890 4798908.340000000000 4798908.45 4798908.8909800 diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_stable.expected.debug b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_stable.expected.debug index b2782d93d..66a98b208 100644 --- a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_stable.expected.debug +++ b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_stable.expected.debug @@ -18,8 +18,12 @@ ____ ^ no match for key ^ no match for key +-0 +__ CARAvan ^ no match for key +-0 +__ ^ no match for key @@ -28,6 +32,8 @@ CARAvan ^ no match for key 000 ___ +-0 +__ 1 _ 00000001 @@ -44,14 +50,14 @@ _____ __ 46.89 _____ +576,446.890 +___ +576,446.88800000 +___ 4567. _____ >>>>37800 _____ -576,446.88800000 -________________ -576,446.890 -___________ 4798908.340000000000 ____________________ 4798908.45 diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_stable.txt b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_stable.txt index a5813ea3a..44667ef03 100644 --- a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_stable.txt +++ b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_stable.txt @@ -17,7 +17,9 @@ -2028789030 -896689 +-0 CARAvan +-0 -8.90880 -.05 @@ -28,3 +30,4 @@ CARAvan 8.013 000 +-0 \ No newline at end of file diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique.expected b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique.expected index cb27c6664..cd4256c5f 100644 --- a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique.expected +++ b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique.expected @@ -11,10 +11,9 @@ 8.013 45 46.89 +576,446.890 4567. 37800 -576,446.88800000 -576,446.890 4798908.340000000000 4798908.45 4798908.8909800 diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique.expected.debug b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique.expected.debug index 782a77724..663a4b3a9 100644 --- a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique.expected.debug +++ b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique.expected.debug @@ -24,14 +24,12 @@ _____ __ 46.89 _____ +576,446.890 +___ 4567. _____ >>>>37800 _____ -576,446.88800000 -________________ -576,446.890 -___________ 4798908.340000000000 ____________________ 4798908.45 diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse.expected b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse.expected index bbce16934..97e261f14 100644 --- a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse.expected +++ b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse.expected @@ -1,10 +1,9 @@ 4798908.8909800 4798908.45 4798908.340000000000 -576,446.890 -576,446.88800000 37800 4567. +576,446.890 46.89 45 8.013 diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse.expected.debug b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse.expected.debug index e0389c1d5..01f7abf5b 100644 --- a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse.expected.debug +++ b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse.expected.debug @@ -4,14 +4,12 @@ _______________ __________ 4798908.340000000000 ____________________ -576,446.890 -___________ -576,446.88800000 -________________ >>>>37800 _____ 4567. _____ +576,446.890 +___ 46.89 _____ 45 diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse_stable.expected b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse_stable.expected deleted file mode 100644 index bbce16934..000000000 --- a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse_stable.expected +++ /dev/null @@ -1,20 +0,0 @@ -4798908.8909800 -4798908.45 -4798908.340000000000 -576,446.890 -576,446.88800000 - 37800 - 4567. -46.89 -45 -8.013 -1.58590 -1.444 -1.040000000 -1 - --.05 --1 --8.90880 --896689 --2028789030 diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_stable.expected b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_stable.expected deleted file mode 100644 index bbce16934..000000000 --- a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_stable.expected +++ /dev/null @@ -1,20 +0,0 @@ -4798908.8909800 -4798908.45 -4798908.340000000000 -576,446.890 -576,446.88800000 - 37800 - 4567. -46.89 -45 -8.013 -1.58590 -1.444 -1.040000000 -1 - --.05 --1 --8.90880 --896689 --2028789030 diff --git a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_stable.txt b/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_stable.txt deleted file mode 100644 index a5813ea3a..000000000 --- a/tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_stable.txt +++ /dev/null @@ -1,30 +0,0 @@ -576,446.890 -576,446.88800000 - - - 4567. -45 -46.89 --1 -1 -00000001 -4798908.340000000000 -4798908.45 -4798908.8909800 - - - 37800 - --2028789030 --896689 -CARAvan - --8.90880 --.05 -1.444 -1.58590 -1.040000000 - -8.013 - -000 diff --git a/tests/fixtures/sort/month_default.expected b/tests/fixtures/sort/month_default.expected index dc51f5d5e..99baa235a 100644 --- a/tests/fixtures/sort/month_default.expected +++ b/tests/fixtures/sort/month_default.expected @@ -1,3 +1,4 @@ + asdf N/A Ut enim ad minim veniam, quis Jan Lorem ipsum dolor sit amet mar laboris nisi ut aliquip ex ea diff --git a/tests/fixtures/sort/month_default.expected.debug b/tests/fixtures/sort/month_default.expected.debug index 2c55a0e2a..f28ba1c48 100644 --- a/tests/fixtures/sort/month_default.expected.debug +++ b/tests/fixtures/sort/month_default.expected.debug @@ -1,3 +1,6 @@ + asdf + ^ no match for key +_____ N/A Ut enim ad minim veniam, quis ^ no match for key _________________________________ diff --git a/tests/fixtures/sort/month_default.txt b/tests/fixtures/sort/month_default.txt index 6d64bf4f8..c96bad300 100644 --- a/tests/fixtures/sort/month_default.txt +++ b/tests/fixtures/sort/month_default.txt @@ -8,3 +8,4 @@ mar laboris nisi ut aliquip ex ea Jul 2 these three lines Jul 1 should remain 2,1,3 Jul 3 if --stable is provided + asdf diff --git a/tests/fixtures/sort/month_stable.expected b/tests/fixtures/sort/month_stable.expected index 868728a18..cb3a8da8f 100644 --- a/tests/fixtures/sort/month_stable.expected +++ b/tests/fixtures/sort/month_stable.expected @@ -1,4 +1,5 @@ N/A Ut enim ad minim veniam, quis + asdf Jan Lorem ipsum dolor sit amet mar laboris nisi ut aliquip ex ea May sed do eiusmod tempor incididunt diff --git a/tests/fixtures/sort/month_stable.expected.debug b/tests/fixtures/sort/month_stable.expected.debug index 4163ba39a..740f9187c 100644 --- a/tests/fixtures/sort/month_stable.expected.debug +++ b/tests/fixtures/sort/month_stable.expected.debug @@ -1,5 +1,7 @@ N/A Ut enim ad minim veniam, quis ^ no match for key + asdf + ^ no match for key Jan Lorem ipsum dolor sit amet ___ mar laboris nisi ut aliquip ex ea diff --git a/tests/fixtures/sort/month_stable.txt b/tests/fixtures/sort/month_stable.txt index 6d64bf4f8..c96bad300 100644 --- a/tests/fixtures/sort/month_stable.txt +++ b/tests/fixtures/sort/month_stable.txt @@ -8,3 +8,4 @@ mar laboris nisi ut aliquip ex ea Jul 2 these three lines Jul 1 should remain 2,1,3 Jul 3 if --stable is provided + asdf diff --git a/tests/fixtures/sort/multiple_decimals_numeric.expected b/tests/fixtures/sort/multiple_decimals_numeric.expected index 3ef4d22e8..8f42e7ce5 100644 --- a/tests/fixtures/sort/multiple_decimals_numeric.expected +++ b/tests/fixtures/sort/multiple_decimals_numeric.expected @@ -21,6 +21,8 @@ CARAvan 8.013 45 46.89 +576,446.88800000 +576,446.890 4567..457 4567. 4567.1 @@ -28,8 +30,6 @@ CARAvan 37800 45670.89079.098 45670.89079.1 -576,446.88800000 -576,446.890 4798908.340000000000 4798908.45 4798908.8909800 diff --git a/tests/fixtures/sort/multiple_decimals_numeric.expected.debug b/tests/fixtures/sort/multiple_decimals_numeric.expected.debug index f40ade9aa..948c4869c 100644 --- a/tests/fixtures/sort/multiple_decimals_numeric.expected.debug +++ b/tests/fixtures/sort/multiple_decimals_numeric.expected.debug @@ -67,6 +67,12 @@ __ 46.89 _____ _____ +576,446.88800000 +___ +________________ +576,446.890 +___ +___________ >>>>>>>>>>4567..457 _____ ___________________ @@ -88,12 +94,6 @@ _____________________ >>>>>>45670.89079.1 ___________ ___________________ -576,446.88800000 -________________ -________________ -576,446.890 -___________ -___________ 4798908.340000000000 ____________________ ____________________