Crate clap_digest

source ·
Expand description

Integration to choose digest with clap on a CLI.

Features

  1. A clap::ValueEnum implementation for the different Digest algorithm types:

    use clap::builder::{Arg, ArgAction, EnumValueParser};
    use clap_digest::Digest;
    
    let digest = Arg::new("digest")
        .action(ArgAction::Set)
        .value_parser(EnumValueParser::<Digest>::new());
  2. Ready-to-use clap::Arg implementations:

    use clap::Command;
    
    let cli = Command::new("myapp")
        .arg(clap_digest::arg::digest().required_unless_present("list-digests"))
        .arg(clap_digest::arg::list_digests());

    See the crate::arg module for more information.

  3. A conversion from crate::Digest to digest::DynDigest:

    use clap_digest::{Digest, DynDigest};
    
    // fn doing some hashing, using any DynDigest implementation
    fn dyn_hash(hasher: &mut dyn DynDigest, data: &[u8]) -> String {
        hasher.update(data);
        let hash = hasher.finalize_reset();
        hash.iter().map(|byte| format!("{:02x}", byte)).collect()
    }
    
    // parse user-supplied CLI input to clap_digest::Digest with clap
    // suppose user runs this with: `command --digest MD5`
    // let args = cli.get_matches();
    let digest = *args.get_one::<Digest>("digest").unwrap();
    
    // convert to DynDigest
    let mut digest: Box<dyn DynDigest> = digest.into();
    
    // use with hashing function
    let hash = dyn_hash(digest.as_mut(), b"foo");
    
    assert_eq!(hash, "acbd18db4cc2f85cedef654fccc4a4d8");
  4. Digest algorithm groups are feature-gated. Use cargo feature clap-digest for a complete listing. At least one digest algorithm group feature must be chosen. To limit the digest algorithm families you want to support in your crate, define your own features, e.g.:

    [features]
    default = ["sha2"]
    md5 = ["clap-digest/md5"]
    sha1 = ["clap-digest/sha1"]
    sha2 = ["clap-digest/sha2"]
    ...
    

Example

And now a complete CLI example (see also in examples/cksum.rs):

use std::path::{Path, PathBuf};

use anyhow::Result;
use clap::{value_parser, Arg, ArgAction, Command, ValueEnum};
use clap_digest::Digest;
use digest::DynDigest;

fn hash_path(path: impl AsRef<Path>, hasher: &mut dyn DynDigest) -> Result<Box<[u8]>> {
    let content = std::fs::read_to_string(path)?;
    let bytes = content.as_bytes();
    hasher.update(bytes);
    Ok(hasher.finalize_reset())
}

fn main() -> Result<()> {
    let args = cli().get_matches();

    if args.get_flag("list-digests") {
        for digest in Digest::value_variants() {
            println!("{digest}");
        }
    } else {
        let inputs = args
            .get_many::<PathBuf>("input")
            .expect("at least one input is required via clap");

        let digest = *args
            .get_one::<Digest>("digest")
            .expect("has default via clap");

        let mut digest: Box<dyn DynDigest> = digest.into();

        for input in inputs {
            let hash = hash_path(input, digest.as_mut())?;
            let hash: String = hash.iter().map(|byte| format!("{byte:02x}")).collect();

            println!("{hash}  {}", input.display());
        }
    }

    Ok(())
}

fn cli() -> Command {
    let input = Arg::new("input")
        .help("input files")
        .required_unless_present("list-digests")
        .action(ArgAction::Append)
        .value_parser(value_parser!(PathBuf));

    Command::new("cksum")
        .arg(input)
        .arg(clap_digest::arg::digest().required_unless_present("list-digests"))
        .arg(clap_digest::arg::list_digests())
        .about("simple cksum clone that hashes text files")
        .after_help("try `cargo run --example cksum -- -d MD5 Cargo.toml | md5sum -c`")
}

A few example runs:

$ cargo run --example cksum -- -d SHA1 Cargo.toml
7a96ee85606435fe1f39c3fa6bdf4cf9bbbc338c  Cargo.toml

$ sha1sum Cargo.toml
7a96ee85606435fe1f39c3fa6bdf4cf9bbbc338c  Cargo.toml

$ cargo run --example cksum -- -d MD5 Cargo.toml | md5sum -c
Cargo.toml: OK

List all supported algorithms:

$ cargo run --example cksum -- --list-digests
BLAKE2b512
BLAKE2s256
BLAKE3
...

All algorithm groups are feature-gated, so you can choose:

$ cargo run --example cksum --no-default-features --features md5,sha1,sha2 -- --list-digests
MD5
SHA1
SHA224
SHA256
SHA384
SHA512
SHA512/224
SHA512/256

Modules

Contains ready-to-use clap::Arg implementations.

Enums

Supported digest algorithms.

Traits

Modification of the Digest trait suitable for trait objects.