Crate clap_digest
source ·Expand description
Integration to choose digest with clap on a CLI.
Features
-
A
clap::ValueEnum
implementation for the differentDigest
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());
-
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. -
A conversion from
crate::Digest
todigest::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");
-
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
Enums
Supported digest algorithms.