use clap::{Parser, ValueEnum};
use ring::{digest, test};
use std::fs::File;
use std::io::{Read, Result};
#[derive(ValueEnum, Clone, Copy, Debug)]
enum DigestType {
SHA1,
SHA256,
SHA384,
SHA512,
SHA512_256,
}
impl DigestType {
fn digest(self) -> &'static digest::Algorithm {
match self {
DigestType::SHA1 => &digest::SHA1_FOR_LEGACY_USE_ONLY,
DigestType::SHA256 => &digest::SHA256,
DigestType::SHA384 => &digest::SHA384,
DigestType::SHA512 => &digest::SHA512,
DigestType::SHA512_256 => &digest::SHA512_256,
}
}
}
#[derive(Parser, Debug)]
#[command(author, version, name = "digest")]
struct Cli {
#[arg(short, long, value_enum)]
digest: Option<DigestType>,
files: Vec<String>,
}
const BUFFER_SIZE: usize = 4096;
fn process(
digest_alg: &'static digest::Algorithm,
file: &mut dyn Read,
name: &str,
) -> Result<digest::Digest> {
let mut digest_context = digest::Context::new(digest_alg);
let mut buffer = [0u8; BUFFER_SIZE];
loop {
let result = file.read(&mut buffer);
match result {
Ok(0) => {
let digest = digest_context.finish();
println!("{} {}", test::to_hex(digest.as_ref()), name);
return Ok(digest);
}
Ok(n) => {
digest_context.update(&buffer[0..n]);
}
Err(e) => {
return Err(e);
}
}
}
}
fn main() -> Result<()> {
let cli = Cli::parse();
let digest_alg = cli.digest.unwrap_or(DigestType::SHA1).digest();
let mut error = None;
if cli.files.is_empty() {
if let Err(e) = process(digest_alg, &mut std::io::stdin(), "-") {
println!("digest: -: {e}");
error = Some(e);
}
} else {
for file_name in cli.files {
if let Err(e) = File::open(&file_name)
.and_then(|mut file| process(digest_alg, &mut file, &file_name))
{
println!("digest: {}: {}", &file_name, e);
error = Some(e);
}
}
}
if let Some(e) = error {
Err(e)
} else {
Ok(())
}
}