From 26cc6a13b478266fccce3115bbce3246b98ea4af Mon Sep 17 00:00:00 2001 From: Luke Street Date: Wed, 24 Jan 2024 23:21:55 -0700 Subject: [PATCH] shasum: Support `-o` in hash mode Fixes #32 --- src/cmd/shasum.rs | 48 +++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/cmd/shasum.rs b/src/cmd/shasum.rs index 05608bb..e600dbb 100644 --- a/src/cmd/shasum.rs +++ b/src/cmd/shasum.rs @@ -1,15 +1,16 @@ use std::{ fs::File, - io::{BufRead, BufReader, Read}, + io::{stdout, BufRead, BufReader, Read, Write}, path::{Path, PathBuf}, }; use anyhow::{anyhow, bail, Context, Result}; use argp::FromArgs; use owo_colors::{OwoColorize, Stream}; +use path_slash::PathExt; use sha1::{Digest, Sha1}; -use crate::util::file::{open_file, process_rsp, touch}; +use crate::util::file::{buf_writer, open_file, process_rsp, touch}; #[derive(FromArgs, PartialEq, Eq, Debug)] /// Print or check SHA1 (160-bit) checksums. @@ -22,7 +23,8 @@ pub struct Args { /// path to input file(s) files: Vec, #[argp(option, short = 'o')] - /// touch output file on successful check + /// (check) touch output file on successful check + /// (hash) write hash(es) to output file output: Option, #[argp(switch, short = 'q')] /// only print failures and a summary @@ -32,17 +34,28 @@ pub struct Args { const DEFAULT_BUF_SIZE: usize = 8192; pub fn run(args: Args) -> Result<()> { - for path in process_rsp(&args.files)? { - let mut file = open_file(&path)?; - if args.check { - check(&args, &mut BufReader::new(file))? - } else { - hash(&mut file, &path)? + if args.check { + for path in process_rsp(&args.files)? { + let file = open_file(&path)?; + check(&args, &mut BufReader::new(file))?; + } + if let Some(out_path) = &args.output { + touch(out_path) + .with_context(|| format!("Failed to touch output file '{}'", out_path.display()))?; + } + } else { + let mut w: Box = + if let Some(out_path) = &args.output { + Box::new(buf_writer(out_path).with_context(|| { + format!("Failed to open output file '{}'", out_path.display()) + })?) + } else { + Box::new(stdout()) + }; + for path in process_rsp(&args.files)? { + let mut file = open_file(&path)?; + hash(w.as_mut(), &mut file, &path)? } - } - if let Some(out_path) = args.output { - touch(&out_path) - .with_context(|| format!("Failed to touch output file '{}'", out_path.display()))?; } Ok(()) } @@ -98,13 +111,16 @@ where R: BufRead + ?Sized { Ok(()) } -fn hash(reader: &mut R, path: &Path) -> Result<()> -where R: Read + ?Sized { +fn hash(w: &mut W, reader: &mut R, path: &Path) -> Result<()> +where + R: Read + ?Sized, + W: Write + ?Sized, +{ let hash = file_sha1(reader)?; let mut hash_buf = [0u8; 40]; let hash_str = base16ct::lower::encode_str(&hash, &mut hash_buf) .map_err(|e| anyhow!("Failed to encode hash: {e}"))?; - println!("{} {}", hash_str, path.display()); + writeln!(w, "{} {}", hash_str, path.to_slash_lossy())?; Ok(()) }