diff --git a/.cargo/README.md b/.cargo/README.md new file mode 100644 index 0000000..e652c71 --- /dev/null +++ b/.cargo/README.md @@ -0,0 +1,47 @@ +# chksum-sha2-224 + +[![GitHub](https://img.shields.io/badge/github-chksum--rs%2Fsha2--224-24292e?style=flat-square&logo=github "GitHub")](https://github.com/chksum-rs/sha2-224) +[![Build](https://img.shields.io/github/actions/workflow/status/chksum-rs/sha2-224/rust.yml?branch=master&style=flat-square&logo=github "Build")](https://github.com/chksum-rs/sha2-224/actions/workflows/rust.yml) +[![docs.rs](https://img.shields.io/docsrs/chksum-sha2-224?style=flat-square&logo=docsdotrs "docs.rs")](https://docs.rs/chksum-sha2-224/) +[![MSRV](https://img.shields.io/badge/MSRV-1.70.0-informational?style=flat-square "MSRV")](https://github.com/chksum-rs/sha2-224/blob/master/Cargo.toml) +[![deps.rs](https://deps.rs/crate/chksum-sha2-224/0.0.0/status.svg?style=flat-square "deps.rs")](https://deps.rs/crate/chksum-sha2-224/0.0.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/chksum-rs/sha2-224?style=flat-square "LICENSE")](https://github.com/chksum-rs/sha2-224/blob/master/LICENSE) + +An implementation of the SHA-2 224 hash function with a straightforward interface for computing digests of bytes, files, directories, and more. + +## Setup + +To use this crate, add the following entry to your `Cargo.toml` file in the `dependencies` section: + +```toml +[dependencies] +chksum-sha2-224 = "0.0.0" +``` + +Alternatively, you can use the [`cargo add`](https://doc.rust-lang.org/cargo/commands/cargo-add.html) subcommand: + +```shell +cargo add chksum-sha2-224 +``` + +## Usage + +Use the `chksum` function to calcualate digest of file, directory and so on. + +```rust +use chksum_sha2_224 as sha2_224; + +let file = File::open(path)?; +let digest = sha2_224::chksum(file)?; +assert_eq!( + digest.to_hex_lowercase(), + "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +); +``` + +For more usage examples, refer to the documentation available at [docs.rs](https://docs.rs/chksum-sha2-224/). + +## License + +This crate is licensed under the MIT License. diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..1b8e198 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,141 @@ +name: Rust + +env: + CARGO_TERM_COLOR: always + +permissions: + contents: read + +on: + push: + branches: + - master + paths: + - ".github/workflows/*.yml" + - "Cargo.toml" + - "src/**.rs" + - "tests/**.rs" + pull_request: + branches: + - master + 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 + + build-and-test-linux: + needs: + - lint + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + toolchain: [1.70.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: + - lint + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + toolchain: [1.70.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: + - lint + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + toolchain: [1.70.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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8e725f7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Cargo +Cargo.lock +target/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..efe5d53 --- /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.0.0] - 2023-12-21 + +### Added + +- Initial release. + +[0.0.0]: https://github.com/chksum-rs/sha2-224/releases/tag/v0.0.0 diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..810d866 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "chksum-sha2-224" +version = "0.0.0" +authors = ["Konrad Goławski "] +edition = "2021" +rust-version = "1.70.0" +description = "An implementation of the SHA-2 224 hash function with a straightforward interface for computing digests of bytes, files, directories, and more." +readme = ".cargo/README.md" +repository = "https://github.com/chksum-rs/sha2-224" +license = "MIT" +keywords = ["checksum", "digest", "hash", "sha224", "sha2-224"] +categories = ["algorithms", "cryptography", "filesystem"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[dependencies] +chksum-core = "0.0.0" +chksum-hash-sha2-224 = "0.0.0" +chksum-reader = { version = "0.0.0", optional = true } +chksum-writer = { version = "0.0.0", optional = true } + +[dev-dependencies] +assert_fs = { version = "1.0.13", features = ["color-auto"] } +thiserror = "1.0.51" + +[features] +default = [] +reader = ["chksum-reader"] +writer = ["chksum-writer"] 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..df39982 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# chksum-sha2-224 + +[![crates.io](https://img.shields.io/crates/v/chksum-sha2-224?style=flat-square&logo=rust "crates.io")](https://crates.io/crates/chksum-sha2-224) +[![Build](https://img.shields.io/github/actions/workflow/status/chksum-rs/sha2-224/rust.yml?branch=master&style=flat-square&logo=github "Build")](https://github.com/chksum-rs/sha2-224/actions/workflows/rust.yml) +[![docs.rs](https://img.shields.io/docsrs/chksum-sha2-224?style=flat-square&logo=docsdotrs "docs.rs")](https://docs.rs/chksum-sha2-224/) +[![MSRV](https://img.shields.io/badge/MSRV-1.70.0-informational?style=flat-square "MSRV")](https://github.com/chksum-rs/sha2-224/blob/master/Cargo.toml) +[![deps.rs](https://deps.rs/crate/chksum-sha2-224/0.0.0/status.svg?style=flat-square "deps.rs")](https://deps.rs/crate/chksum-sha2-224/0.0.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/chksum-rs/sha2-224?style=flat-square "LICENSE")](https://github.com/chksum-rs/sha2-224/blob/master/LICENSE) + +An implementation of the SHA-2 224 hash function with a straightforward interface for computing digests of bytes, files, directories, and more. + +## Setup + +To use this crate, add the following entry to your `Cargo.toml` file in the `dependencies` section: + +```toml +[dependencies] +chksum-sha2-224 = "0.0.0" +``` + +Alternatively, you can use the [`cargo add`](https://doc.rust-lang.org/cargo/commands/cargo-add.html) subcommand: + +```shell +cargo add chksum-sha2-224 +``` + +## Usage + +Use the `chksum` function to calcualate digest of file, directory and so on. + +```rust +use chksum_sha2_224 as sha2_224; + +let file = File::open(path)?; +let digest = sha2_224::chksum(file)?; +assert_eq!( + digest.to_hex_lowercase(), + "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +); +``` + +For more usage examples, refer to the documentation available at [docs.rs](https://docs.rs/chksum-sha2-224/). + +## License + +This crate is licensed under the MIT License. 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..a9a514a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,597 @@ +//! This crate provides an implementation of the SHA-2 224 hash function with a straightforward interface for computing digests of bytes, files, directories, and more. +//! +//! For a low-level interface, you can explore the [`chksum_hash_sha2_224`] crate. +//! +//! # Setup +//! +//! To use this crate, add the following entry to your `Cargo.toml` file in the `dependencies` section: +//! +//! ```toml +//! [dependencies] +//! chksum-sha2-224 = "0.0.0" +//! ``` +//! +//! Alternatively, you can use the [`cargo add`](https://doc.rust-lang.org/cargo/commands/cargo-add.html) subcommand: +//! +//! ```sh +//! cargo add chksum-sha2-224 +//! ``` +//! +//! # Usage +//! +//! Use the [`chksum`] function to calcualate digest of file, directory and so on. +//! +//! ```rust +//! # use std::path::Path; +//! use std::fs::File; +//! +//! # use chksum_sha2_224::Result; +//! use chksum_sha2_224 as sha2_224; +//! +//! # fn wrapper(path: &Path) -> Result<()> { +//! let file = File::open(path)?; +//! let digest = sha2_224::chksum(file)?; +//! assert_eq!( +//! digest.to_hex_lowercase(), +//! "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +//! ); +//! # Ok(()) +//! # } +//! ``` +//! +//! # Input Types +//! +//! ## Bytes +//! +//! ### Array +//! +//! ```rust +//! # use chksum_sha2_224::Result; +//! use chksum_sha2_224 as sha2_224; +//! +//! # fn wrapper() -> Result<()> { +//! let data = [0, 1, 2, 3]; +//! let digest = sha2_224::chksum(data)?; +//! assert_eq!( +//! digest.to_hex_lowercase(), +//! "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +//! ); +//! # Ok(()) +//! # } +//! ``` +//! +//! ### Vec +//! +//! ```rust +//! # use chksum_sha2_224::Result; +//! use chksum_sha2_224 as sha2_224; +//! +//! # fn wrapper() -> Result<()> { +//! let data = vec![0, 1, 2, 3]; +//! let digest = sha2_224::chksum(data)?; +//! assert_eq!( +//! digest.to_hex_lowercase(), +//! "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +//! ); +//! # Ok(()) +//! # } +//! ``` +//! +//! ### Slice +//! +//! ```rust +//! # use chksum_sha2_224::Result; +//! use chksum_sha2_224 as sha2_224; +//! +//! # fn wrapper() -> Result<()> { +//! let data = &[0, 1, 2, 3]; +//! let digest = sha2_224::chksum(data)?; +//! assert_eq!( +//! digest.to_hex_lowercase(), +//! "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +//! ); +//! # Ok(()) +//! # } +//! ``` +//! +//! ## Strings +//! +//! ### str +//! +//! ```rust +//! # use chksum_sha2_224::Result; +//! use chksum_sha2_224 as sha2_224; +//! +//! # fn wrapper() -> Result<()> { +//! let data = "&str"; +//! let digest = sha2_224::chksum(data)?; +//! assert_eq!( +//! digest.to_hex_lowercase(), +//! "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +//! ); +//! # Ok(()) +//! # } +//! ``` +//! +//! ### String +//! +//! ```rust +//! # use chksum_sha2_224::Result; +//! use chksum_sha2_224 as sha2_224; +//! +//! # fn wrapper() -> Result<()> { +//! let data = String::from("String"); +//! let digest = sha2_224::chksum(data)?; +//! assert_eq!( +//! digest.to_hex_lowercase(), +//! "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +//! ); +//! # Ok(()) +//! # } +//! ``` +//! +//! ## File +//! +//! ```rust +//! # use std::path::Path; +//! use std::fs::File; +//! +//! # use chksum_sha2_224::Result; +//! use chksum_sha2_224 as sha2_224; +//! +//! # fn wrapper(path: &Path) -> Result<()> { +//! let file = File::open(path)?; +//! let digest = sha2_224::chksum(file)?; +//! assert_eq!( +//! digest.to_hex_lowercase(), +//! "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +//! ); +//! # Ok(()) +//! # } +//! ``` +//! +//! ## Directory +//! +//! ```rust +//! # use std::path::Path; +//! use std::fs::read_dir; +//! +//! # use chksum_sha2_224::Result; +//! use chksum_sha2_224 as sha2_224; +//! +//! # fn wrapper(path: &Path) -> Result<()> { +//! let readdir = read_dir(path)?; +//! let digest = sha2_224::chksum(readdir)?; +//! assert_eq!( +//! digest.to_hex_lowercase(), +//! "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +//! ); +//! # Ok(()) +//! # } +//! ``` +//! +//! ## Path +//! +//! ```rust +//! # use std::path::Path; +//! use std::path::PathBuf; +//! +//! # use chksum_sha2_224::Result; +//! use chksum_sha2_224 as sha2_224; +//! +//! # fn wrapper(path: &Path) -> Result<()> { +//! let path = PathBuf::from(path); +//! let digest = sha2_224::chksum(path)?; +//! assert_eq!( +//! digest.to_hex_lowercase(), +//! "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +//! ); +//! # Ok(()) +//! # } +//! ``` +//! +//! ## Standard Input +//! +//! ```rust +//! use std::io::stdin; +//! +//! # use chksum_sha2_224::Result; +//! use chksum_sha2_224 as sha2_224; +//! +//! # fn wrapper() -> Result<()> { +//! let stdin = stdin(); +//! let digest = sha2_224::chksum(stdin)?; +//! assert_eq!( +//! digest.to_hex_lowercase(), +//! "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +//! ); +//! # Ok(()) +//! # } +//! ``` +//! +//! # Features +//! +//! Cargo features are utilized to enable extra options. +//! +//! * `reader` enables the [`reader`] module with the [`Reader`] struct. +//! * `writer` enables the [`writer`] module with the [`Writer`] struct. +//! +//! By default, neither of these features is enabled. +//! +//! To customize your setup, disable the default features and enable only those that you need in your `Cargo.toml` file: +//! +//! ```toml +//! [dependencies] +//! chksum-sha2-224 = { version = "0.0.0", features = ["reader", "writer"] } +//! ``` +//! +//! Alternatively, you can use the [`cargo add`](https://doc.rust-lang.org/cargo/commands/cargo-add.html) subcommand: +//! +//! ```shell +//! cargo add chksum-sha2-224 --features reader,writer +//! ``` +//! +//! # License +//! +//! This crate is licensed under the MIT License. + +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![forbid(unsafe_code)] + +#[cfg(feature = "reader")] +pub mod reader; +#[cfg(feature = "writer")] +pub mod writer; + +use std::fmt::{self, Display, Formatter, LowerHex, UpperHex}; + +use chksum_core as core; +#[doc(no_inline)] +pub use chksum_core::{Chksumable, Error, Hash, Hashable, Result}; +#[doc(no_inline)] +pub use chksum_hash_sha2_224 as hash; + +#[cfg(feature = "reader")] +#[doc(inline)] +pub use crate::reader::Reader; +#[cfg(feature = "writer")] +#[doc(inline)] +pub use crate::writer::Writer; + +/// Creates a new hash. +/// +/// # Example +/// +/// ```rust +/// use chksum_sha2_224 as sha2_224; +/// +/// let mut hash = sha2_224::new(); +/// hash.update(b"example data"); +/// let digest = hash.digest(); +/// assert_eq!( +/// digest.to_hex_lowercase(), +/// "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +/// ); +/// ``` +#[must_use] +pub fn new() -> SHA2_224 { + SHA2_224::new() +} + +/// Creates a default hash. +/// +/// # Example +/// +/// ```rust +/// use chksum_sha2_224 as sha2_224; +/// +/// let mut hash = sha2_224::default(); +/// hash.update(b"example data"); +/// let digest = hash.digest(); +/// assert_eq!( +/// digest.to_hex_lowercase(), +/// "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +/// ); +/// ``` +#[must_use] +pub fn default() -> SHA2_224 { + core::default() +} + +/// Computes the hash of the given input. +/// +/// # Example +/// +/// ```rust +/// use chksum_sha2_224 as sha2_224; +/// +/// let data = b"example data"; +/// let digest = sha2_224::hash(data); +/// assert_eq!( +/// digest.to_hex_lowercase(), +/// "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +/// ); +/// ``` +pub fn hash(data: impl core::Hashable) -> Digest { + core::hash::(data) +} + +/// Computes the hash of the given input. +/// +/// # Example +/// +/// ```rust +/// use chksum_sha2_224 as sha2_224; +/// +/// let data = b"example data"; +/// if let Ok(digest) = sha2_224::chksum(data) { +/// assert_eq!( +/// digest.to_hex_lowercase(), +/// "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +/// ); +/// } +/// ``` +pub fn chksum(data: impl core::Chksumable) -> Result { + core::chksum::(data) +} + +/// The SHA-2 224 hash instance. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct SHA2_224 { + inner: hash::Update, +} + +impl SHA2_224 { + /// Calculates the hash digest of an input data. + /// + /// # Example + /// + /// ```rust + /// use chksum_sha2_224::SHA2_224; + /// + /// let data = b"example data"; + /// let digest = SHA2_224::hash(data); + /// assert_eq!( + /// digest.to_hex_lowercase(), + /// "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" + /// ); + /// ``` + #[must_use] + pub fn hash(data: T) -> Digest + where + T: AsRef<[u8]>, + { + let mut hash = Self::new(); + hash.update(data); + hash.digest() + } + + /// Creates a new hash. + /// + /// # Example + /// + /// ```rust + /// use chksum_sha2_224::SHA2_224; + /// + /// let mut hash = SHA2_224::new(); + /// hash.update(b"example data"); + /// let digest = hash.digest(); + /// assert_eq!( + /// digest.to_hex_lowercase(), + /// "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" + /// ); + /// ``` + #[must_use] + pub fn new() -> Self { + let inner = hash::Update::new(); + Self { inner } + } + + /// Updates the hash state with an input data. + /// + /// # Example + /// + /// ```rust + /// use chksum_sha2_224::SHA2_224; + /// + /// let mut hash = SHA2_224::new(); + /// hash.update(b"example"); + /// hash.update(" "); + /// hash.update("data"); + /// let digest = hash.digest(); + /// assert_eq!( + /// digest.to_hex_lowercase(), + /// "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" + /// ); + /// ``` + pub fn update(&mut self, data: T) + where + T: AsRef<[u8]>, + { + self.inner.update(data); + } + + /// Resets the hash state to its initial state. + /// + /// # Example + /// + /// ```rust + /// use chksum_sha2_224::SHA2_224; + /// + /// let mut hash = SHA2_224::new(); + /// hash.update(b"example data"); + /// hash.reset(); + /// let digest = hash.digest(); + /// assert_eq!( + /// digest.to_hex_lowercase(), + /// "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" + /// ); + /// ``` + pub fn reset(&mut self) { + self.inner.reset(); + } + + /// Produces the hash digest. + /// + /// # Example + /// + /// ``` + /// use chksum_sha2_224::SHA2_224; + /// + /// let mut hash = SHA2_224::new(); + /// let digest = hash.digest(); + /// assert_eq!( + /// digest.to_hex_lowercase(), + /// "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" + /// ); + /// ``` + #[must_use] + pub fn digest(&self) -> Digest { + self.inner.digest().into() + } +} + +impl core::Hash for SHA2_224 { + type Digest = Digest; + + fn update(&mut self, data: T) + where + T: AsRef<[u8]>, + { + self.update(data); + } + + fn reset(&mut self) { + self.reset(); + } + + fn digest(&self) -> Self::Digest { + self.digest() + } +} + +/// A hash digest. +pub struct Digest(hash::Digest); + +impl Digest { + /// Creates a new digest. + #[must_use] + pub const fn new(digest: [u8; hash::DIGEST_LENGTH_BYTES]) -> Self { + let inner = hash::Digest::new(digest); + Self(inner) + } + + /// Returns a byte slice of the digest's contents. + #[must_use] + pub const fn as_bytes(&self) -> &[u8] { + let Self(inner) = self; + inner.as_bytes() + } + + /// Consumes the digest, returning the digest bytes. + #[must_use] + pub fn into_inner(self) -> [u8; hash::DIGEST_LENGTH_BYTES] { + let Self(inner) = self; + inner.into_inner() + } + + /// Returns a string in the lowercase hexadecimal representation. + /// + /// # Example + /// + /// ```rust + /// use chksum_sha2_224 as sha2_224; + /// + /// #[rustfmt::skip] + /// let digest = [ + /// 0xD1, 0x4A, 0x02, 0x8C, + /// 0x2A, 0x3A, 0x2B, 0xC9, + /// 0x47, 0x61, 0x02, 0xBB, + /// 0x28, 0x82, 0x34, 0xC4, + /// 0x15, 0xA2, 0xB0, 0x1F, + /// 0x82, 0x8E, 0xA6, 0x2A, + /// 0xC5, 0xB3, 0xE4, 0x2F, + /// ]; + /// let digest = sha2_224::Digest::new(digest); + /// assert_eq!( + /// digest.to_hex_lowercase(), + /// "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" + /// ); + /// ``` + #[must_use] + pub fn to_hex_lowercase(&self) -> String { + let Self(inner) = self; + inner.to_hex_lowercase() + } + + /// Returns a string in the uppercase hexadecimal representation. + /// + /// # Example + /// + /// ```rust + /// use chksum_sha2_224 as sha2_224; + /// + /// #[rustfmt::skip] + /// let digest = [ + /// 0xD1, 0x4A, 0x02, 0x8C, + /// 0x2A, 0x3A, 0x2B, 0xC9, + /// 0x47, 0x61, 0x02, 0xBB, + /// 0x28, 0x82, 0x34, 0xC4, + /// 0x15, 0xA2, 0xB0, 0x1F, + /// 0x82, 0x8E, 0xA6, 0x2A, + /// 0xC5, 0xB3, 0xE4, 0x2F, + /// ]; + /// let digest = sha2_224::Digest::new(digest); + /// assert_eq!( + /// digest.to_hex_uppercase(), + /// "D14A028C2A3A2BC9476102BB288234C415A2B01F828EA62AC5B3E42F" + /// ); + /// ``` + #[must_use] + pub fn to_hex_uppercase(&self) -> String { + let Self(inner) = self; + inner.to_hex_uppercase() + } +} + +impl core::Digest for Digest {} + +impl AsRef<[u8]> for Digest { + fn as_ref(&self) -> &[u8] { + let Self(inner) = self; + inner.as_bytes() + } +} + +impl Display for Digest { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let Self(inner) = self; + Display::fmt(inner, f) + } +} + +impl LowerHex for Digest { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let Self(inner) = self; + LowerHex::fmt(inner, f) + } +} + +impl UpperHex for Digest { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let Self(inner) = self; + UpperHex::fmt(inner, f) + } +} + +impl From<[u8; hash::DIGEST_LENGTH_BYTES]> for Digest { + fn from(digest: [u8; hash::DIGEST_LENGTH_BYTES]) -> Self { + Self::new(digest) + } +} + +impl From for Digest { + fn from(digest: hash::Digest) -> Self { + Self(digest) + } +} diff --git a/src/reader.rs b/src/reader.rs new file mode 100644 index 0000000..3ed891e --- /dev/null +++ b/src/reader.rs @@ -0,0 +1,70 @@ +//! This module is optional and can be enabled using the `reader` Cargo feature. +//! +//! The [`Reader`] allows on-the-fly calculation of the digest while reading the data. +//! +//! # Enabling +//! +//! Add the following entry to your `Cargo.toml` file to enable the `reader` feature: +//! +//! ```toml +//! [dependencies] +//! chksum-sha2-224 = { version = "0.0.0", features = ["reader"] } +//! ``` +//! +//! Alternatively, use the [`cargo add`](https://doc.rust-lang.org/cargo/commands/cargo-add.html) subcommand: +//! +//! ```shell +//! cargo add chksum-sha2-224 --features reader +//! ``` +//! +//! # Example +//! +//! ```rust +//! # use std::path::Path; +//! use std::fs::File; +//! use std::io::Read; // required by reader +//! +//! # use chksum_sha2_224::Result; +//! use chksum_sha2_224 as sha2_224; +//! +//! # fn wrapper(path: &Path) -> Result<()> { +//! let file = File::open(path)?; +//! let mut reader = sha2_224::reader::new(file); +//! +//! let mut buffer = Vec::new(); +//! reader.read_to_end(&mut buffer)?; +//! assert_eq!(buffer, b"example data"); +//! +//! let digest = reader.digest(); +//! assert_eq!( +//! digest.to_hex_lowercase(), +//! "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +//! ); +//! # Ok(()) +//! # } +//! ``` + +use std::io::Read; + +use chksum_reader as reader; + +use crate::SHA2_224; + +/// A specialized [`Reader`](reader::Reader) type with the [`SHA2_224`] hash algorithm. +pub type Reader = reader::Reader; + +/// Creates new [`Reader`]. +pub fn new(inner: R) -> Reader +where + R: Read, +{ + reader::new(inner) +} + +/// Creates new [`Reader`] with provided hash. +pub fn with_hash(inner: R, hash: SHA2_224) -> Reader +where + R: Read, +{ + reader::with_hash(inner, hash) +} diff --git a/src/writer.rs b/src/writer.rs new file mode 100644 index 0000000..211ac46 --- /dev/null +++ b/src/writer.rs @@ -0,0 +1,62 @@ +//! This module is optional and can be enabled using the `writer` Cargo feature. +//! +//! The [`Writer`] allows on-the-fly calculation of the digest while writing the data. +//! +//! # Enabling +//! +//! Add the following entry to your `Cargo.toml` file to enable the `writer` feature: +//! +//! ```toml +//! [dependencies] +//! chksum-sha2-224 = { version = "0.0.0", features = ["writer"] } +//! ``` +//! +//! Alternatively, use the [`cargo add`](https://doc.rust-lang.org/cargo/commands/cargo-add.html) subcommand: +//! +//! ```shell +//! cargo add chksum-sha2-224 --features writer +//! ``` +//! +//! # Example +//! +//! ```rust +//! # use std::path::Path; +//! use std::fs::File; +//! use std::io::Write; // required by writer +//! +//! # use chksum_sha2_224::Result; +//! use chksum_sha2_224 as sha2_224; +//! +//! # fn wrapper(path: &Path) -> Result<()> { +//! let file = File::open(path)?; +//! let mut writer = sha2_224::writer::new(file); +//! +//! writer.write_all(b"example data")?; +//! +//! let digest = writer.digest(); +//! assert_eq!( +//! digest.to_hex_lowercase(), +//! "90382cbfda2656313ad61fd74b32ddfa4bcc118f660bd4fba9228ced" +//! ); +//! # Ok(()) +//! # } +//! ``` + +use std::io::Write; + +use chksum_writer as writer; + +use crate::SHA2_224; + +/// A specialized [`Writer`](writer::Writer) type with the [`SHA2_224`] hash algorithm. +pub type Writer = writer::Writer; + +/// Creates new [`Writer`]. +pub fn new(inner: impl Write) -> Writer { + writer::new(inner) +} + +/// Creates new [`Writer`] with provided hash. +pub fn with_hash(inner: impl Write, hash: SHA2_224) -> Writer { + writer::with_hash(inner, hash) +} diff --git a/tests/sha2_224.rs b/tests/sha2_224.rs new file mode 100644 index 0000000..bd11b6b --- /dev/null +++ b/tests/sha2_224.rs @@ -0,0 +1,273 @@ +use std::fs::{read_dir, File}; +use std::io::Error as IoError; + +use assert_fs::fixture::FixtureError; +use assert_fs::prelude::{FileTouch, FileWriteBin, PathChild}; +use assert_fs::TempDir; +use chksum_sha2_224::{chksum, Error as ChksumError}; + +#[derive(Debug, thiserror::Error)] +enum Error { + #[error(transparent)] + ChksumError(#[from] ChksumError), + #[error(transparent)] + FixtureError(#[from] FixtureError), + #[error(transparent)] + IoError(#[from] IoError), +} + +#[test] +fn empty_directory_as_path() -> Result<(), Error> { + let temp_dir = TempDir::new()?; + + let dir = temp_dir.path(); + let digest = chksum(dir)?.to_hex_lowercase(); + assert_eq!(digest, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + + Ok(()) +} + +#[test] +fn empty_directory_as_pathbuf() -> Result<(), Error> { + let temp_dir = TempDir::new()?; + + let dir = temp_dir.to_path_buf(); + let digest = chksum(dir)?.to_hex_lowercase(); + assert_eq!(digest, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + + let dir = &temp_dir.to_path_buf(); + let digest = chksum(dir)?.to_hex_lowercase(); + assert_eq!(digest, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + + Ok(()) +} + +#[test] +fn empty_directory_as_readdir() -> Result<(), Error> { + let temp_dir = TempDir::new()?; + + let dir = read_dir(temp_dir.path())?; + let digest = chksum(dir)?.to_hex_lowercase(); + assert_eq!(digest, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + + Ok(()) +} + +#[test] +fn non_empty_directory_with_empty_file_as_path() -> Result<(), Error> { + let temp_dir = { + let temp_dir = TempDir::new()?; + temp_dir.child("file.txt").touch()?; + temp_dir + }; + + let dir = temp_dir.path(); + let digest = chksum(dir)?.to_hex_lowercase(); + assert_eq!(digest, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + + Ok(()) +} + +#[test] +fn non_empty_directory_with_empty_file_as_pathbuf() -> Result<(), Error> { + let temp_dir = { + let temp_dir = TempDir::new()?; + temp_dir.child("file.txt").touch()?; + temp_dir + }; + + let dir = temp_dir.to_path_buf(); + let digest = chksum(dir)?.to_hex_lowercase(); + assert_eq!(digest, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + + let dir = &temp_dir.to_path_buf(); + let digest = chksum(dir)?.to_hex_lowercase(); + assert_eq!(digest, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + + Ok(()) +} + +#[test] +fn non_empty_directory_with_empty_file_as_readdir() -> Result<(), Error> { + let temp_dir = { + let temp_dir = TempDir::new()?; + temp_dir.child("file.txt").touch()?; + temp_dir + }; + + let dir = read_dir(temp_dir.path())?; + let digest = chksum(dir)?.to_hex_lowercase(); + assert_eq!(digest, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + + Ok(()) +} + +#[test] +fn non_empty_directory_with_non_empty_file_as_path() -> Result<(), Error> { + let temp_dir = { + let temp_dir = TempDir::new()?; + let file = temp_dir.child("file.txt"); + file.touch()?; + file.write_binary(b"data")?; + temp_dir + }; + + let dir = temp_dir.path(); + let digest = chksum(dir)?.to_hex_lowercase(); + assert_eq!(digest, "f4739673acc03c424343b452787ee23dd62999a8a9f14f4250995769"); + + Ok(()) +} + +#[test] +fn non_empty_directory_with_non_empty_file_as_pathbuf() -> Result<(), Error> { + let temp_dir = { + let temp_dir = TempDir::new()?; + let file = temp_dir.child("file.txt"); + file.touch()?; + file.write_binary(b"data")?; + temp_dir + }; + + let dir = temp_dir.to_path_buf(); + let digest = chksum(dir)?.to_hex_lowercase(); + assert_eq!(digest, "f4739673acc03c424343b452787ee23dd62999a8a9f14f4250995769"); + + let dir = &temp_dir.to_path_buf(); + let digest = chksum(dir)?.to_hex_lowercase(); + assert_eq!(digest, "f4739673acc03c424343b452787ee23dd62999a8a9f14f4250995769"); + + Ok(()) +} + +#[test] +fn non_empty_directory_with_non_empty_file_as_readdir() -> Result<(), Error> { + let temp_dir = { + let temp_dir = TempDir::new()?; + let file = temp_dir.child("file.txt"); + file.touch()?; + file.write_binary(b"data")?; + temp_dir + }; + + let dir = read_dir(temp_dir.path())?; + let digest = chksum(dir)?.to_hex_lowercase(); + assert_eq!(digest, "f4739673acc03c424343b452787ee23dd62999a8a9f14f4250995769"); + + Ok(()) +} + +#[test] +fn empty_file_as_path() -> Result<(), Error> { + let temp_dir = TempDir::new()?; + let child = { + let file = temp_dir.child("file.txt"); + file.touch()?; + file + }; + + let file = child.path(); + let digest = chksum(file)?.to_hex_lowercase(); + assert_eq!(digest, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + + Ok(()) +} + +#[test] +fn empty_file_as_pathbuf() -> Result<(), Error> { + let temp_dir = TempDir::new()?; + let child = { + let file = temp_dir.child("file.txt"); + file.touch()?; + file + }; + + let file = child.to_path_buf(); + let digest = chksum(file)?.to_hex_lowercase(); + assert_eq!(digest, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + + let file = &child.to_path_buf(); + let digest = chksum(file)?.to_hex_lowercase(); + assert_eq!(digest, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + + Ok(()) +} + +#[test] +fn empty_file_as_file() -> Result<(), Error> { + let temp_dir = TempDir::new()?; + let child = { + let file = temp_dir.child("file.txt"); + file.touch()?; + file + }; + + let file = File::open(child.path())?; + let digest = chksum(file)?.to_hex_lowercase(); + assert_eq!(digest, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + + let file = &File::open(child.path())?; + let digest = chksum(file)?.to_hex_lowercase(); + assert_eq!(digest, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + + Ok(()) +} + +#[test] +fn non_empty_file_as_path() -> Result<(), Error> { + let temp_dir = TempDir::new()?; + let child = { + let file = temp_dir.child("file.txt"); + file.touch()?; + file.write_binary(b"data")?; + file + }; + + let file = child.path(); + let digest = chksum(file)?.to_hex_lowercase(); + assert_eq!(digest, "f4739673acc03c424343b452787ee23dd62999a8a9f14f4250995769"); + + Ok(()) +} + +#[test] +fn non_empty_file_as_pathbuf() -> Result<(), Error> { + let temp_dir = TempDir::new()?; + let child = { + let file = temp_dir.child("file.txt"); + file.touch()?; + file.write_binary(b"data")?; + file + }; + + let file = child.to_path_buf(); + let digest = chksum(file)?.to_hex_lowercase(); + assert_eq!(digest, "f4739673acc03c424343b452787ee23dd62999a8a9f14f4250995769"); + + let file = &child.to_path_buf(); + let digest = chksum(file)?.to_hex_lowercase(); + assert_eq!(digest, "f4739673acc03c424343b452787ee23dd62999a8a9f14f4250995769"); + + Ok(()) +} + +#[test] +fn non_empty_file_as_file() -> Result<(), Error> { + let temp_dir = TempDir::new()?; + let child = { + let file = temp_dir.child("file.txt"); + file.touch()?; + file.write_binary(b"data")?; + file + }; + + let file = File::open(child.path())?; + let digest = chksum(file)?.to_hex_lowercase(); + assert_eq!(digest, "f4739673acc03c424343b452787ee23dd62999a8a9f14f4250995769"); + + let file = &File::open(child.path())?; + let digest = chksum(file)?.to_hex_lowercase(); + assert_eq!(digest, "f4739673acc03c424343b452787ee23dd62999a8a9f14f4250995769"); + + Ok(()) +}