From 6171d71f1eb7de58e2b2f83fcc94d8f7cc4f9238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konrad=20Go=C5=82awski?= Date: Sun, 13 Aug 2023 00:25:09 +0200 Subject: [PATCH] Initial commit --- .cargo/README.md | 105 ++++ .github/workflows/rust.yml | 217 ++++++++ .gitignore | 2 + CHANGELOG.md | 14 + Cargo.lock | 1027 ++++++++++++++++++++++++++++++++++++ Cargo.toml | 35 ++ LICENSE | 21 + README.md | 106 ++++ deny.toml | 108 ++++ rustfmt.toml | 17 + src/lib.rs | 143 +++++ src/main.rs | 85 +++ tests/cli.rs | 32 ++ tests/common.rs | 17 + tests/md5.rs | 116 ++++ tests/sha1.rs | 101 ++++ tests/sha2_224.rs | 113 ++++ tests/sha2_256.rs | 113 ++++ tests/sha2_384.rs | 113 ++++ tests/sha2_512.rs | 113 ++++ 20 files changed, 2598 insertions(+) create mode 100644 .cargo/README.md create mode 100644 .github/workflows/rust.yml create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 deny.toml create mode 100644 rustfmt.toml create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 tests/cli.rs create mode 100644 tests/common.rs create mode 100644 tests/md5.rs create mode 100644 tests/sha1.rs create mode 100644 tests/sha2_224.rs create mode 100644 tests/sha2_256.rs create mode 100644 tests/sha2_384.rs create mode 100644 tests/sha2_512.rs diff --git a/.cargo/README.md b/.cargo/README.md new file mode 100644 index 0000000..2f6602f --- /dev/null +++ b/.cargo/README.md @@ -0,0 +1,105 @@ +# chksum-cli + +[![GitHub](https://img.shields.io/badge/github-ferric--bytes%2Fchksum--cli-24292e?style=flat-square&logo=github "GitHub")](https://github.com/ferric-bytes/chksum-cli) +[![Coverage](https://img.shields.io/codecov/c/gh/ferric-bytes/chksum-cli?style=flat-square&logo=codecov "Coverage")](https://app.codecov.io/gh/ferric-bytes/chksum-cli) +[![MSRV](https://img.shields.io/badge/MSRV-1.66.0-informational?style=flat-square "MSRV")](https://github.com/ferric-bytes/chksum-cli/blob/master/Cargo.toml) +[![deps.rs](https://deps.rs/crate/chksum-cli/0.2.0/status.svg?style=flat-square "deps.rs")](https://deps.rs/crate/chksum-cli/0.2.0) +[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg?style=flat-square "unsafe forbidden")](https://github.com/rust-secure-code/safety-dance) +[![LICENSE](https://img.shields.io/github/license/ferric-bytes/chksum-cli?style=flat-square "LICENSE")](https://github.com/ferric-bytes/chksum-cli/blob/master/LICENSE) + +A simple checksum calculator. + +## Motivation + +There are variety of tools that allows calculate hash digests. + +However tools like `md5sum`, `sha1sum`, `b2sum`, `sha224sum` and others offer only file-based checksums. + +```shell +find dir/ -type f | sort | xargs cat | sha224sum +``` + +Instead you can just use `chksum` with preffered hash algorithm. + +```sh +chksum sha2-224 dir/ +``` + +## Features + +- Written in pure Rust +- No unsafe code +- Multithread + +## Installation + +Use [`cargo install`](https://doc.rust-lang.org/cargo/commands/cargo-install.html) to install `chksum` binary in `$HOME/.cargo/bin` directory. + +```shell +cargo install chksum-cli +``` + +## Usage + +```shell +$ chksum help +A simple checksum calculator. + +Usage: chksum + +Commands: + md5 Calculate MD5 digest + sha1 Calculate SHA-1 digest + sha2-224 Calculate SHA-2 224 digest + sha2-256 Calculate SHA-2 256 digest + sha2-384 Calculate SHA-2 384 digest + sha2-512 Calculate SHA-2 512 digest + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help + -V, --version Print version +``` + +```shell +$ chksum help sha2-224 +Calculate SHA-2 224 digest + +Usage: chksum sha2-224 ... + +Arguments: + ... Path to file or directory + +Options: + -h, --help Print help +``` + +```shell +$ chksum sha2-224 LICENSE +99258bca0d23c69388dd53412f1009132753b89459359a401a6ed158 LICENSE +``` + +```shell +$ chksum sha1 src/ +598c9268d2078e12bc0c32ff40ebb8ee9f8351ea src/ +``` + +## Library + +Check [`chksum`](https://crates.io/crates/chksum) crate to see the library that allows you to calculate digests of files and directories with easy-to-use interface. + +## Hash algorithms + +Implemented hash algorithms: + +* MD5 - [RFC 1321: The MD5 Message-Digest Algorithm](https://tools.ietf.org/html/rfc1321) +* SHA-1 - [RFC 3174: US Secure Hash Algorithm 1 (SHA1)](https://tools.ietf.org/html/rfc3174) +* SHA-2 family (SHA-224, SHA-256, SHA-386, SHA-512) - [FIPS PUB 180-4: Secure Hash Standard](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf) + +## Disclaimer + +Code is under development. The interface may change in the future. + +## License + +MIT diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..cebd0ad --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,217 @@ +name: Rust + +env: + CARGO_TERM_COLOR: always + +permissions: + contents: read + +on: + push: + branches: + - master + - dev + paths: + - ".github/workflows/*.yml" + - "Cargo.toml" + - "src/**.rs" + - "tests/**.rs" + pull_request: + branches: + - master + - dev + paths: + - ".github/workflows/*.yml" + - "Cargo.toml" + - "src/**.rs" + - "tests/**.rs" + +jobs: + lint: + runs-on: ubuntu-latest + name: Lint + permissions: + checks: write + contents: write + pull-requests: write + steps: + - name: Repository checkout + uses: actions/checkout@v3 + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + default: true + profile: minimal + components: rustfmt, clippy + - name: Run cargo fmt + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --check --verbose + - name: Run cargo clippy + uses: actions-rs/clippy-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --all-features -- --deny clippy::cargo + + deny: + runs-on: ubuntu-latest + name: Deny + steps: + - name: Repository checkout + uses: actions/checkout@v3 + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + default: true + profile: minimal + - name: Install cargo deny + uses: actions-rs/install@v0.1 + with: + crate: cargo-deny + - name: Run cargo deny + uses: actions-rs/cargo@v1 + with: + command: deny + args: --all-features check + + security-audit: + name: Security Audit + runs-on: ubuntu-latest + steps: + - name: Repository checkout + uses: actions/checkout@v3 + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + default: true + profile: minimal + - name: Run audit check + uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + build-and-test-linux: + needs: + - deny + - lint + - security-audit + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + toolchain: [1.66.0, stable, nightly] + name: "Build and test (OS: Linux, Toolchain: ${{ matrix.toolchain }})" + steps: + - name: Repository checkout + uses: actions/checkout@v3 + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.toolchain }} + default: true + profile: minimal + - name: Run cargo build + uses: actions-rs/cargo@v1 + with: + command: build + args: --all-features --verbose + - name: Run cargo test + uses: actions-rs/cargo@v1 + with: + command: test + args: --all-features --verbose + + build-and-test-macos: + needs: + - deny + - lint + - security-audit + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + toolchain: [1.66.0, stable, nightly] + name: "Build and test (OS: MacOS, Toolchain: ${{ matrix.toolchain }})" + steps: + - name: Repository checkout + uses: actions/checkout@v3 + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.toolchain }} + default: true + profile: minimal + - name: Run cargo build + uses: actions-rs/cargo@v1 + with: + command: build + args: --all-features --verbose + - name: Run cargo test + uses: actions-rs/cargo@v1 + with: + command: test + args: --all-features --verbose + + build-and-test-windows: + needs: + - deny + - lint + - security-audit + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + toolchain: [1.66.0, stable, nightly] + name: "Build and test (OS: Windows, Toolchain: ${{ matrix.toolchain }})" + steps: + - name: Repository checkout + uses: actions/checkout@v3 + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.toolchain }} + default: true + profile: minimal + - name: Run cargo build + uses: actions-rs/cargo@v1 + with: + command: build + args: --all-features --verbose + - name: Run cargo test + uses: actions-rs/cargo@v1 + with: + command: test + args: --all-features --verbose + + coverage: + needs: + - deny + - lint + - security-audit + runs-on: ubuntu-latest + name: Coverage + steps: + - name: Repository checkout + uses: actions/checkout@v3 + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + default: true + profile: minimal + - name: Install cargo tarpaulin + uses: actions-rs/install@v0.1 + with: + crate: cargo-tarpaulin + - name: Run cargo tarpaulin + run: cargo tarpaulin --all-features --fail-under 50 --follow-exec --ignore-tests --out xml --timeout 120 + - name: Upload to codecov.io + if: ${{ always() }} + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..36a351c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Cargo +target/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5f72a61 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.2.0] - 2023-08-13 + +### Added + +- Initial release. + +[0.2.0]: https://github.com/ferric-bytes/chksum/releases/tag/v0.2.0 diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b99176b --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1027 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" + +[[package]] +name = "assert_cmd" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" +dependencies = [ + "anstream", + "anstyle", + "bstr", + "doc-comment", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "assert_fs" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f070617a68e5c2ed5d06ee8dd620ee18fb72b99f6c094bed34cf8ab07c875b48" +dependencies = [ + "anstream", + "anstyle", + "doc-comment", + "globwalk", + "predicates", + "predicates-core", + "predicates-tree", + "tempfile", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "bstr" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "cc" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chksum" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457d1a0b14ce2c411ff2509218fd57bb014ccd4146551bc5939eaad88bb14d10" +dependencies = [ + "anyhow", + "chksum-build", + "chksum-hash", + "is-terminal", + "thiserror", +] + +[[package]] +name = "chksum-build" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f8fc7a29ee6db3b8d673e449b975d3070493e2324cbccc23adae43fa91b1b2c" +dependencies = [ + "anyhow", + "chrono", + "nom", + "thiserror", +] + +[[package]] +name = "chksum-cli" +version = "0.2.0" +dependencies = [ + "anyhow", + "assert_cmd", + "assert_fs", + "chksum", + "clap", + "exitcode", + "rayon", + "thiserror", +] + +[[package]] +name = "chksum-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d1ebc18540020919bdc8052b7d37f4944da7de32e50de232b64c262b177948d" +dependencies = [ + "anyhow", + "chksum-build", + "thiserror", +] + +[[package]] +name = "chrono" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "winapi", +] + +[[package]] +name = "clap" +version = "4.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "once_cell", + "strsim", + "terminal_size", + "unicase", + "unicode-width", +] + +[[package]] +name = "clap_derive" +version = "4.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "errno" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "exitcode" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" + +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "globset" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "iana-time-zone" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ignore" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" +dependencies = [ + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix 0.38.8", + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "predicates" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" +dependencies = [ + "anstyle", + "difflib", + "itertools", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "rustix" +version = "0.37.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys", +] + +[[package]] +name = "rustix" +version = "0.38.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys 0.4.5", + "windows-sys", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix 0.38.8", + "windows-sys", +] + +[[package]] +name = "terminal_size" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237" +dependencies = [ + "rustix 0.37.23", + "windows-sys", +] + +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + +[[package]] +name = "thiserror" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a62ae28 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "chksum-cli" +version = "0.2.0" +authors = ["Konrad Goławski "] +edition = "2021" +rust-version = "1.66.0" +description = "A simple checksum calculator." +readme = ".cargo/README.md" +repository = "https://github.com/ferric-bytes/chksum-cli" +license = "MIT" +keywords = ["checksum", "digest", "directory-checksum", "file-checksum", "hash"] +categories = ["command-line-utilities", "cryptography", "filesystem"] + +[profile.release] +lto = "fat" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[dependencies] +anyhow = "1.0.72" +chksum = "0.2.0" +clap = { version = "4.3.19", features = ["cargo", "derive", "wrap_help", "unicode"] } +exitcode = "1.1.2" +rayon = "1.7.0" + +[dev-dependencies] +assert_cmd = { version = "2.0.12", features = ["color-auto"] } +assert_fs = { version = "1.0.13", features = ["color-auto"] } +thiserror = "1.0.44" + +[[bin]] +name = "chksum" +path = "src/main.rs" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..10c2349 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Konrad Goławski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..eca243e --- /dev/null +++ b/README.md @@ -0,0 +1,106 @@ +# chksum-cli + +[![Build](https://img.shields.io/github/actions/workflow/status/ferric-bytes/chksum-cli/rust.yml?branch=master&style=flat-square&logo=github "Build")](https://github.com/ferric-bytes/chksum-cli/actions/workflows/rust.yml) +[![crates.io](https://img.shields.io/crates/v/chksum-cli?style=flat-square&logo=rust "crates.io")](https://crates.io/crates/chksum-cli) +[![Coverage](https://img.shields.io/codecov/c/gh/ferric-bytes/chksum-cli?style=flat-square&logo=codecov "Coverage")](https://app.codecov.io/gh/ferric-bytes/chksum-cli) +[![MSRV](https://img.shields.io/badge/MSRV-1.66.0-informational?style=flat-square "MSRV")](https://github.com/ferric-bytes/chksum-cli/blob/master/Cargo.toml) +[![deps.rs](https://deps.rs/crate/chksum-cli/0.2.0/status.svg?style=flat-square "deps.rs")](https://deps.rs/crate/chksum-cli/0.2.0) +[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg?style=flat-square "unsafe forbidden")](https://github.com/rust-secure-code/safety-dance) +[![LICENSE](https://img.shields.io/github/license/ferric-bytes/chksum-cli?style=flat-square "LICENSE")](https://github.com/ferric-bytes/chksum-cli/blob/master/LICENSE) + +A simple checksum calculator. + +## Motivation + +There are variety of tools that allows calculate hash digests. + +However tools like `md5sum`, `sha1sum`, `b2sum`, `sha224sum` and others offer only file-based checksums. + +```shell +find dir/ -type f | sort | xargs cat | sha224sum +``` + +Instead you can just use `chksum` with preffered hash algorithm. + +```sh +chksum sha2-224 dir/ +``` + +## Features + +- Written in pure Rust +- No unsafe code +- Multithread + +## Installation + +Use [`cargo install`](https://doc.rust-lang.org/cargo/commands/cargo-install.html) to install `chksum` binary in `$HOME/.cargo/bin` directory. + +```shell +cargo install chksum-cli +``` + +## Usage + +```shell +$ chksum help +A simple checksum calculator. + +Usage: chksum + +Commands: + md5 Calculate MD5 digest + sha1 Calculate SHA-1 digest + sha2-224 Calculate SHA-2 224 digest + sha2-256 Calculate SHA-2 256 digest + sha2-384 Calculate SHA-2 384 digest + sha2-512 Calculate SHA-2 512 digest + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help + -V, --version Print version +``` + +```shell +$ chksum help sha2-224 +Calculate SHA-2 224 digest + +Usage: chksum sha2-224 ... + +Arguments: + ... Path to file or directory + +Options: + -h, --help Print help +``` + +```shell +$ chksum sha2-224 LICENSE +99258bca0d23c69388dd53412f1009132753b89459359a401a6ed158 LICENSE +``` + +```shell +$ chksum sha1 src/ +598c9268d2078e12bc0c32ff40ebb8ee9f8351ea src/ +``` + +## Library + +Check [`chksum`](https://github.com/ferric-bytes/chksum) repository to see the library that allows you to calculate digests of files and directories with easy-to-use interface. + +## Hash algorithms + +Implemented hash algorithms: + +* MD5 - [RFC 1321: The MD5 Message-Digest Algorithm](https://tools.ietf.org/html/rfc1321) +* SHA-1 - [RFC 3174: US Secure Hash Algorithm 1 (SHA1)](https://tools.ietf.org/html/rfc3174) +* SHA-2 family (SHA-224, SHA-256, SHA-386, SHA-512) - [FIPS PUB 180-4: Secure Hash Standard](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf) + +## Disclaimer + +Code is under development. The interface may change in the future. + +## License + +MIT diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000..e2bad86 --- /dev/null +++ b/deny.toml @@ -0,0 +1,108 @@ +# If 1 or more target triples (and optionally, target_features) are specified, +# only the specified targets will be checked when running `cargo deny check`. +# This means, if a particular package is only ever used as a target specific +# dependency, such as, for example, the `nix` crate only being used via the +# `target_family = "unix"` configuration, that only having windows targets in +# this list would mean the nix crate, as well as any of its exclusive +# dependencies not shared by any other crates, would be ignored, as the target +# list here is effectively saying which targets you are building for. +targets = [ + # The triple can be any string, but only the target triples built in to + # rustc (as of 1.40) can be checked against actual config expressions + #{ triple = "x86_64-unknown-linux-musl" }, + # You can also specify which target_features you promise are enabled for a + # particular target. target_features are currently not validated against + # the actual valid features supported by the target architecture. + #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, +] + +# This section is considered when running `cargo deny check advisories` +# More documentation for the advisories section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html +[advisories] +# The path where the advisory database is cloned/fetched into +db-path = "~/.cargo/advisory-db" +# The url(s) of the advisory databases to use +db-urls = ["https://github.com/rustsec/advisory-db"] +# The lint level for security vulnerabilities +vulnerability = "deny" +# The lint level for unmaintained crates +unmaintained = "warn" +# The lint level for crates that have been yanked from their source registry +yanked = "warn" +# The lint level for crates with security notices. Note that as of +# 2019-12-17 there are no security notice advisories in +# https://github.com/rustsec/advisory-db +notice = "warn" + +# If this is true, then cargo deny will use the git executable to fetch advisory database. +# If this is false, then it uses a built-in git library. +# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support. +# See Git Authentication for more information about setting up git authentication. +git-fetch-with-cli = true + +# This section is considered when running `cargo deny check licenses` +# More documentation for the licenses section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html +[licenses] +# The lint level for crates which do not have a detectable license +unlicensed = "deny" +# List of explicitly allowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. +allow = [ + "Apache-2.0", + "MIT", +] +# Lint level for licenses considered copyleft +copyleft = "warn" +# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses +# * both - The license will be approved if it is both OSI-approved *AND* FSF +# * either - The license will be approved if it is either OSI-approved *OR* FSF +# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF +# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved +# * neither - This predicate is ignored and the default lint level is used +allow-osi-fsf-free = "either" +# Lint level used when no other predicates are matched +# 1. License isn't in the allow or deny lists +# 2. License isn't copyleft +# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" +default = "deny" + +[licenses.private] +# If true, ignores workspace crates that aren't published, or are only +# published to private registries. +# To see how to mark a crate as unpublished (to the official registry), +# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field. +ignore = false + +# This section is considered when running `cargo deny check bans`. +# More documentation about the 'bans' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html +[bans] +# Lint level for when multiple versions of the same crate are detected +multiple-versions = "warn" +# Lint level for when a crate version requirement is `*` +wildcards = "allow" +# The graph highlighting used when creating dotgraphs for crates +# with multiple versions +# * lowest-version - The path to the lowest versioned duplicate is highlighted +# * simplest-path - The path to the version with the fewest edges is highlighted +# * all - Both lowest-version and simplest-path are used +highlight = "all" + +# This section is considered when running `cargo deny check sources`. +# More documentation about the 'sources' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html +[sources] +# Lint level for what to happen when a crate from a crate registry that is not +# in the allow list is encountered +unknown-registry = "deny" +# Lint level for what to happen when a crate from a git repository that is not +# in the allow list is encountered +unknown-git = "deny" +# List of URLs for allowed crate registries. Defaults to the crates.io index +# if not specified. If it is specified but empty, no registries are allowed. +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +# List of URLs for allowed Git repositories +allow-git = [] diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..7a65644 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,17 @@ +combine_control_expr = false +edition = "2021" +force_multiline_blocks = true +format_code_in_doc_comments = true +format_generated_files = true +format_strings = true +group_imports = "StdExternalCrate" +hex_literal_case = "Upper" +imports_granularity = "Module" +imports_layout = "HorizontalVertical" +match_block_trailing_comma = true +max_width = 120 +normalize_comments = true +normalize_doc_attributes = true +reorder_impl_items = true +use_field_init_shorthand = true +use_try_shorthand = true diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..91e8419 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,143 @@ +use std::fmt::Debug; +use std::io::{self, Write}; +use std::path::PathBuf; + +use chksum::hash::Digest; +use chksum::Result; +use exitcode::{IOERR as EXITCODE_IOERR, OK as EXITCODE_OK}; + +#[derive(Debug, clap::Parser)] +#[command(author, version, about, long_about = None)] +pub struct Command { + #[command(subcommand)] + pub subcommand: Subcommand, +} + +#[derive(Debug, clap::Subcommand)] +pub enum Subcommand { + /// Calculate MD5 digest. + #[command(arg_required_else_help = true)] + MD5 { + #[command(flatten)] + args: Args, + #[command(flatten)] + options: Options, + }, + /// Calculate SHA-1 digest. + #[command(arg_required_else_help = true)] + SHA1 { + #[command(flatten)] + args: Args, + #[command(flatten)] + options: Options, + }, + /// Calculate SHA-2 224 digest. + #[command(arg_required_else_help = true)] + SHA2_224 { + #[command(flatten)] + args: Args, + #[command(flatten)] + options: Options, + }, + /// Calculate SHA-2 256 digest. + #[command(arg_required_else_help = true)] + SHA2_256 { + #[command(flatten)] + args: Args, + #[command(flatten)] + options: Options, + }, + /// Calculate SHA-2 384 digest. + #[command(arg_required_else_help = true)] + SHA2_384 { + #[command(flatten)] + args: Args, + #[command(flatten)] + options: Options, + }, + /// Calculate SHA-2 512 digest. + #[command(arg_required_else_help = true)] + SHA2_512 { + #[command(flatten)] + args: Args, + #[command(flatten)] + options: Options, + }, +} + +#[derive(Debug, clap::Args)] +pub struct Args { + /// Path to file or directory. + #[arg(required = true, name = "PATH")] + pub paths: Vec, +} + +#[derive(Debug, clap::Args)] +pub struct Options { + /// Calculate digest from stdin. + #[arg(short, long, default_value_t = false, exclusive = true)] + pub stdin: bool, +} + +/// Prints result to stdout or stderr. +#[inline] +pub fn print_result(stdout: &mut T, stderr: &mut U, target: String, result: Result) -> io::Result<()> +where + T: Write, + U: Write, + V: Digest, +{ + match result { + Ok(digest) => { + writeln!(stdout, "{target}\t{digest:x}") + }, + Err(error) => { + writeln!(stderr, "{target} - {error}") + }, + } +} + +/// Turns result to exitcode. +#[inline] +pub fn exitcode(result: &Result) -> i32 +where + T: Digest, +{ + if result.is_ok() { + EXITCODE_OK + } else { + EXITCODE_IOERR + } +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + use assert_fs::prelude::PathChild; + use assert_fs::TempDir; + use chksum::chksum; + use chksum::hash::MD5; + + use super::*; + + #[test] + fn exitcode_ok() -> Result<()> { + let tmpdir = TempDir::new()?; + + let result = chksum::(tmpdir.path()); + assert_eq!(exitcode(&result), EXITCODE_OK); + + Ok(()) + } + + #[test] + fn exitcode_error() -> Result<()> { + let tmpdir = TempDir::new()?; + let child = tmpdir.child("child"); + + let result = chksum::(child.path()); + assert_eq!(exitcode(&result), EXITCODE_IOERR); + + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..748c253 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,85 @@ +use std::io::{stderr, stdin, stdout, StdinLock, Write}; +use std::path::PathBuf; +use std::sync::mpsc; +use std::{process, thread}; + +use anyhow::Result; +use chksum::hash::{MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512}; +use chksum::{chksum, Chksum, Error}; +use chksum_cli::{exitcode, print_result, Args, Command, Options, Subcommand}; +use clap::Parser; +use exitcode::{OK as EXITCODE_OK, USAGE as EXITCODE_USAGE}; +use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; + +/// Exits process with given return code. +#[inline] +fn exit(code: i32) -> ! { + let _ = stdout().lock().flush(); + let _ = stderr().lock().flush(); + process::exit(code) +} + +/// Handles subcommand execution. +#[inline] +fn subcommand(args: &Args, options: &Options) -> i32 +where + T: for<'a> Chksum<&'a PathBuf, Error = Error> + for<'a> Chksum, Error = Error>, + T::Digest: 'static + Send, +{ + let (tx, rx) = mpsc::sync_channel(1); + + let printer = thread::spawn(move || { + let mut stdout = stdout().lock(); + let mut stderr = stderr().lock(); + while let Ok(pair) = rx.recv() { + let (target, result) = pair; + print_result(&mut stdout, &mut stderr, target, result).expect("Cannot print result"); + } + }); + + let rc = if options.stdin { + let handle = stdin().lock(); + let result = chksum::(handle); + let rc = exitcode(&result); + let pair = ("".to_string(), result); + tx.send(pair).expect("Cannot send result to printer thread"); + rc + } else { + args.paths + .par_iter() + .map(|path| { + let result = chksum::(path); + let rc = exitcode(&result); + let pair = (path.display().to_string(), result); + tx.send(pair).expect("Cannot send result to printer thread"); + rc + }) + // returns first occured error + .reduce(|| EXITCODE_OK, |acc, rc| if acc == EXITCODE_OK { rc } else { acc }) + }; + + drop(tx); // must drop manually, otherwise rx.recv() never return an error + + printer.join().expect("The printer thread has panicked"); + + rc +} + +/// Main function. +fn main() -> Result<()> { + let command = Command::try_parse().unwrap_or_else(|error| { + let _ = error.print(); + exit(EXITCODE_USAGE); + }); + + let rc = match command.subcommand { + Subcommand::MD5 { args, options } => subcommand::(&args, &options), + Subcommand::SHA1 { args, options } => subcommand::(&args, &options), + Subcommand::SHA2_224 { args, options } => subcommand::(&args, &options), + Subcommand::SHA2_256 { args, options } => subcommand::(&args, &options), + Subcommand::SHA2_384 { args, options } => subcommand::(&args, &options), + Subcommand::SHA2_512 { args, options } => subcommand::(&args, &options), + }; + + exit(rc); +} diff --git a/tests/cli.rs b/tests/cli.rs new file mode 100644 index 0000000..5da2222 --- /dev/null +++ b/tests/cli.rs @@ -0,0 +1,32 @@ +use assert_cmd::Command; + +mod common; +use common::Result; + +#[test] +fn no_args() -> Result { + Command::cargo_bin("chksum")?.assert().failure().code(exitcode::USAGE); + + Ok(()) +} + +#[test] +fn help() -> Result { + Command::cargo_bin("chksum")? + .arg("-h") + .assert() + .failure() + .code(exitcode::USAGE); + Command::cargo_bin("chksum")? + .arg("--help") + .assert() + .failure() + .code(exitcode::USAGE); + Command::cargo_bin("chksum")? + .arg("help") + .assert() + .failure() + .code(exitcode::USAGE); + + Ok(()) +} diff --git a/tests/common.rs b/tests/common.rs new file mode 100644 index 0000000..a2603fe --- /dev/null +++ b/tests/common.rs @@ -0,0 +1,17 @@ +use std::result; + +use assert_cmd::cargo::CargoError; +use assert_fs::fixture::FixtureError; +use chksum::Error as ChksumError; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + CargoError(#[from] CargoError), + #[error(transparent)] + ChksumError(#[from] ChksumError), + #[error(transparent)] + FixtureError(#[from] FixtureError), +} + +pub type Result = result::Result<(), Error>; diff --git a/tests/md5.rs b/tests/md5.rs new file mode 100644 index 0000000..95977b5 --- /dev/null +++ b/tests/md5.rs @@ -0,0 +1,116 @@ +use assert_cmd::Command; +use assert_fs::prelude::{FileTouch, PathChild, PathCreateDir}; +use assert_fs::TempDir; + +mod common; +use common::Result; + +#[test] +fn help() -> Result { + Command::cargo_bin("chksum")? + .arg("md5") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("md5") + .arg("-h") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("md5") + .arg("--help") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("help") + .arg("md5") + .assert() + .failure() + .code(exitcode::USAGE); + + Ok(()) +} + +#[test] +fn empty_stdin() -> Result { + Command::cargo_bin("chksum")? + .arg("md5") + .arg("--stdin") + .write_stdin("") + .assert() + .success(); + + Ok(()) +} + +#[test] +fn empty_directory() -> Result { + let tmpdir = TempDir::new()?; + + let dir = tmpdir.child("dir"); + dir.create_dir_all()?; + + Command::cargo_bin("chksum")? + .arg("md5") + .arg(dir.path()) + .assert() + .success(); + + Ok(()) +} + +#[test] +fn empty_file() -> Result { + let tmpdir = TempDir::new()?; + + let file = tmpdir.child("file"); + file.touch()?; + + Command::cargo_bin("chksum")? + .arg("md5") + .arg(file.path()) + .assert() + .success(); + + Ok(()) +} + +#[test] +fn nonexistent_path() -> Result { + let tmpdir = TempDir::new()?; + + let nonexistent = tmpdir.child("nonexistent"); + + Command::cargo_bin("chksum")? + .arg("md5") + .arg(nonexistent.path()) + .assert() + .failure() + .code(exitcode::IOERR); + + Ok(()) +} + +#[test] +fn stdin_and_path() -> Result { + let tmpdir = TempDir::new()?; + + let dir = tmpdir.child("dir"); + dir.create_dir_all()?; + + Command::cargo_bin("chksum")? + .arg("md5") + .arg("--stdin") + .arg(dir.path()) + .assert() + .failure() + .code(exitcode::USAGE); + + Ok(()) +} diff --git a/tests/sha1.rs b/tests/sha1.rs new file mode 100644 index 0000000..d6be3ba --- /dev/null +++ b/tests/sha1.rs @@ -0,0 +1,101 @@ +use assert_cmd::Command; +use assert_fs::prelude::{FileTouch, PathChild, PathCreateDir}; +use assert_fs::TempDir; + +mod common; +use common::Result; + +#[test] +fn help() -> Result { + Command::cargo_bin("chksum")? + .arg("sha1") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("sha1") + .arg("-h") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("sha1") + .arg("--help") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("help") + .arg("sha1") + .assert() + .failure() + .code(exitcode::USAGE); + + Ok(()) +} + +#[test] +fn empty_directory() -> Result { + let tmpdir = TempDir::new()?; + + let dir = tmpdir.child("dir"); + dir.create_dir_all()?; + Command::cargo_bin("chksum")? + .arg("sha1") + .arg(dir.path()) + .assert() + .success(); + + Ok(()) +} + +#[test] +fn empty_file() -> Result { + let tmpdir = TempDir::new()?; + + let file = tmpdir.child("file"); + file.touch()?; + Command::cargo_bin("chksum")? + .arg("sha1") + .arg(file.path()) + .assert() + .success(); + + Ok(()) +} + +#[test] +fn nonexistent_path() -> Result { + let tmpdir = TempDir::new()?; + + let nonexistent = tmpdir.child("nonexistent"); + Command::cargo_bin("chksum")? + .arg("sha1") + .arg(nonexistent.path()) + .assert() + .failure() + .code(exitcode::IOERR); + + Ok(()) +} + +#[test] +fn stdin_and_path() -> Result { + let tmpdir = TempDir::new()?; + + let dir = tmpdir.child("dir"); + dir.create_dir_all()?; + + Command::cargo_bin("chksum")? + .arg("sha1") + .arg("--stdin") + .arg(dir.path()) + .assert() + .failure() + .code(exitcode::USAGE); + + Ok(()) +} diff --git a/tests/sha2_224.rs b/tests/sha2_224.rs new file mode 100644 index 0000000..dd6fa30 --- /dev/null +++ b/tests/sha2_224.rs @@ -0,0 +1,113 @@ +use assert_cmd::Command; +use assert_fs::prelude::{FileTouch, PathChild, PathCreateDir}; +use assert_fs::TempDir; + +mod common; +use common::Result; + +#[test] +fn help() -> Result { + Command::cargo_bin("chksum")? + .arg("sha2-224") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("sha2-224") + .arg("-h") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("sha2-224") + .arg("--help") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("help") + .arg("sha2-224") + .assert() + .failure() + .code(exitcode::USAGE); + + Ok(()) +} + +#[test] +fn empty_stdin() -> Result { + Command::cargo_bin("chksum")? + .arg("sha2-224") + .arg("--stdin") + .write_stdin("") + .assert() + .success(); + + Ok(()) +} + +#[test] +fn empty_directory() -> Result { + let tmpdir = TempDir::new()?; + + let dir = tmpdir.child("dir"); + dir.create_dir_all()?; + Command::cargo_bin("chksum")? + .arg("sha2-224") + .arg(dir.path()) + .assert() + .success(); + + Ok(()) +} + +#[test] +fn empty_file() -> Result { + let tmpdir = TempDir::new()?; + + let file = tmpdir.child("file"); + file.touch()?; + Command::cargo_bin("chksum")? + .arg("sha2-224") + .arg(file.path()) + .assert() + .success(); + + Ok(()) +} + +#[test] +fn nonexistent_path() -> Result { + let tmpdir = TempDir::new()?; + + let nonexistent = tmpdir.child("nonexistent"); + Command::cargo_bin("chksum")? + .arg("sha2-224") + .arg(nonexistent.path()) + .assert() + .failure() + .code(exitcode::IOERR); + + Ok(()) +} + +#[test] +fn stdin_and_path() -> Result { + let tmpdir = TempDir::new()?; + + let dir = tmpdir.child("dir"); + dir.create_dir_all()?; + + Command::cargo_bin("chksum")? + .arg("sha2-224") + .arg("--stdin") + .arg(dir.path()) + .assert() + .failure() + .code(exitcode::USAGE); + + Ok(()) +} diff --git a/tests/sha2_256.rs b/tests/sha2_256.rs new file mode 100644 index 0000000..751175d --- /dev/null +++ b/tests/sha2_256.rs @@ -0,0 +1,113 @@ +use assert_cmd::Command; +use assert_fs::prelude::{FileTouch, PathChild, PathCreateDir}; +use assert_fs::TempDir; + +mod common; +use common::Result; + +#[test] +fn help() -> Result { + Command::cargo_bin("chksum")? + .arg("sha2-256") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("sha2-256") + .arg("-h") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("sha2-256") + .arg("--help") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("help") + .arg("sha2-256") + .assert() + .failure() + .code(exitcode::USAGE); + + Ok(()) +} + +#[test] +fn empty_stdin() -> Result { + Command::cargo_bin("chksum")? + .arg("sha2-256") + .arg("--stdin") + .write_stdin("") + .assert() + .success(); + + Ok(()) +} + +#[test] +fn empty_directory() -> Result { + let tmpdir = TempDir::new()?; + + let dir = tmpdir.child("dir"); + dir.create_dir_all()?; + Command::cargo_bin("chksum")? + .arg("sha2-256") + .arg(dir.path()) + .assert() + .success(); + + Ok(()) +} + +#[test] +fn empty_file() -> Result { + let tmpdir = TempDir::new()?; + + let file = tmpdir.child("file"); + file.touch()?; + Command::cargo_bin("chksum")? + .arg("sha2-256") + .arg(file.path()) + .assert() + .success(); + + Ok(()) +} + +#[test] +fn nonexistent_path() -> Result { + let tmpdir = TempDir::new()?; + + let nonexistent = tmpdir.child("nonexistent"); + Command::cargo_bin("chksum")? + .arg("sha2-256") + .arg(nonexistent.path()) + .assert() + .failure() + .code(exitcode::IOERR); + + Ok(()) +} + +#[test] +fn stdin_and_path() -> Result { + let tmpdir = TempDir::new()?; + + let dir = tmpdir.child("dir"); + dir.create_dir_all()?; + + Command::cargo_bin("chksum")? + .arg("sha2-256") + .arg("--stdin") + .arg(dir.path()) + .assert() + .failure() + .code(exitcode::USAGE); + + Ok(()) +} diff --git a/tests/sha2_384.rs b/tests/sha2_384.rs new file mode 100644 index 0000000..4cc269f --- /dev/null +++ b/tests/sha2_384.rs @@ -0,0 +1,113 @@ +use assert_cmd::Command; +use assert_fs::prelude::{FileTouch, PathChild, PathCreateDir}; +use assert_fs::TempDir; + +mod common; +use common::Result; + +#[test] +fn help() -> Result { + Command::cargo_bin("chksum")? + .arg("sha2-384") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("sha2-384") + .arg("-h") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("sha2-384") + .arg("--help") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("help") + .arg("sha2-384") + .assert() + .failure() + .code(exitcode::USAGE); + + Ok(()) +} + +#[test] +fn empty_stdin() -> Result { + Command::cargo_bin("chksum")? + .arg("sha2-384") + .arg("--stdin") + .write_stdin("") + .assert() + .success(); + + Ok(()) +} + +#[test] +fn empty_directory() -> Result { + let tmpdir = TempDir::new()?; + + let dir = tmpdir.child("dir"); + dir.create_dir_all()?; + Command::cargo_bin("chksum")? + .arg("sha2-384") + .arg(dir.path()) + .assert() + .success(); + + Ok(()) +} + +#[test] +fn empty_file() -> Result { + let tmpdir = TempDir::new()?; + + let file = tmpdir.child("file"); + file.touch()?; + Command::cargo_bin("chksum")? + .arg("sha2-384") + .arg(file.path()) + .assert() + .success(); + + Ok(()) +} + +#[test] +fn nonexistent_path() -> Result { + let tmpdir = TempDir::new()?; + + let nonexistent = tmpdir.child("nonexistent"); + Command::cargo_bin("chksum")? + .arg("sha2-384") + .arg(nonexistent.path()) + .assert() + .failure() + .code(exitcode::IOERR); + + Ok(()) +} + +#[test] +fn stdin_and_path() -> Result { + let tmpdir = TempDir::new()?; + + let dir = tmpdir.child("dir"); + dir.create_dir_all()?; + + Command::cargo_bin("chksum")? + .arg("sha2-384") + .arg("--stdin") + .arg(dir.path()) + .assert() + .failure() + .code(exitcode::USAGE); + + Ok(()) +} diff --git a/tests/sha2_512.rs b/tests/sha2_512.rs new file mode 100644 index 0000000..5c060a3 --- /dev/null +++ b/tests/sha2_512.rs @@ -0,0 +1,113 @@ +use assert_cmd::Command; +use assert_fs::prelude::{FileTouch, PathChild, PathCreateDir}; +use assert_fs::TempDir; + +mod common; +use common::Result; + +#[test] +fn help() -> Result { + Command::cargo_bin("chksum")? + .arg("sha2-512") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("sha2-512") + .arg("-h") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("sha2-512") + .arg("--help") + .assert() + .failure() + .code(exitcode::USAGE); + + Command::cargo_bin("chksum")? + .arg("help") + .arg("sha2-512") + .assert() + .failure() + .code(exitcode::USAGE); + + Ok(()) +} + +#[test] +fn empty_stdin() -> Result { + Command::cargo_bin("chksum")? + .arg("sha2-512") + .arg("--stdin") + .write_stdin("") + .assert() + .success(); + + Ok(()) +} + +#[test] +fn empty_directory() -> Result { + let tmpdir = TempDir::new()?; + + let dir = tmpdir.child("dir"); + dir.create_dir_all()?; + Command::cargo_bin("chksum")? + .arg("sha2-512") + .arg(dir.path()) + .assert() + .success(); + + Ok(()) +} + +#[test] +fn empty_file() -> Result { + let tmpdir = TempDir::new()?; + + let file = tmpdir.child("file"); + file.touch()?; + Command::cargo_bin("chksum")? + .arg("sha2-512") + .arg(file.path()) + .assert() + .success(); + + Ok(()) +} + +#[test] +fn nonexistent_path() -> Result { + let tmpdir = TempDir::new()?; + + let nonexistent = tmpdir.child("nonexistent"); + Command::cargo_bin("chksum")? + .arg("sha2-512") + .arg(nonexistent.path()) + .assert() + .failure() + .code(exitcode::IOERR); + + Ok(()) +} + +#[test] +fn stdin_and_path() -> Result { + let tmpdir = TempDir::new()?; + + let dir = tmpdir.child("dir"); + dir.create_dir_all()?; + + Command::cargo_bin("chksum")? + .arg("sha2-512") + .arg("--stdin") + .arg(dir.path()) + .assert() + .failure() + .code(exitcode::USAGE); + + Ok(()) +}