diff --git a/src/uu/cksum/src/cksum.rs b/src/uu/cksum/src/cksum.rs index 84be146ec..cf95d1bd2 100644 --- a/src/uu/cksum/src/cksum.rs +++ b/src/uu/cksum/src/cksum.rs @@ -13,8 +13,9 @@ use std::iter; use std::path::Path; use uucore::checksum::{ calculate_blake2b_length, detect_algo, digest_reader, perform_checksum_validation, - ChecksumError, ChecksumOptions, ALGORITHM_OPTIONS_BLAKE2B, ALGORITHM_OPTIONS_BSD, - ALGORITHM_OPTIONS_CRC, ALGORITHM_OPTIONS_CRC32B, ALGORITHM_OPTIONS_SYSV, SUPPORTED_ALGORITHMS, + ChecksumError, ChecksumOptions, ChecksumVerbose, ALGORITHM_OPTIONS_BLAKE2B, + ALGORITHM_OPTIONS_BSD, ALGORITHM_OPTIONS_CRC, ALGORITHM_OPTIONS_CRC32B, ALGORITHM_OPTIONS_SYSV, + SUPPORTED_ALGORITHMS, }; use uucore::{ encoding, @@ -322,13 +323,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { || iter::once(OsStr::new("-")).collect::>(), |files| files.map(OsStr::new).collect::>(), ); + + let verbose = ChecksumVerbose::new(status, quiet, warn); + let opts = ChecksumOptions { binary: binary_flag, ignore_missing, - quiet, - status, strict, - warn, + verbose, }; return perform_checksum_validation(files.iter().copied(), algo_option, length, opts); @@ -462,19 +464,22 @@ pub fn uu_app() -> Command { .short('w') .long("warn") .help("warn about improperly formatted checksum lines") - .action(ArgAction::SetTrue), + .action(ArgAction::SetTrue) + .overrides_with_all([options::STATUS, options::QUIET]), ) .arg( Arg::new(options::STATUS) .long("status") .help("don't output anything, status code shows success") - .action(ArgAction::SetTrue), + .action(ArgAction::SetTrue) + .overrides_with_all([options::WARN, options::QUIET]), ) .arg( Arg::new(options::QUIET) .long(options::QUIET) .help("don't print OK for each successfully verified file") - .action(ArgAction::SetTrue), + .action(ArgAction::SetTrue) + .overrides_with_all([options::WARN, options::STATUS]), ) .arg( Arg::new(options::IGNORE_MISSING) diff --git a/src/uu/hashsum/src/hashsum.rs b/src/uu/hashsum/src/hashsum.rs index 1d3a758f5..b8dc63c32 100644 --- a/src/uu/hashsum/src/hashsum.rs +++ b/src/uu/hashsum/src/hashsum.rs @@ -24,6 +24,7 @@ use uucore::checksum::escape_filename; use uucore::checksum::perform_checksum_validation; use uucore::checksum::ChecksumError; use uucore::checksum::ChecksumOptions; +use uucore::checksum::ChecksumVerbose; use uucore::checksum::HashAlgorithm; use uucore::error::{FromIo, UResult}; use uucore::sum::{Digest, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256}; @@ -240,13 +241,14 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> { || iter::once(OsStr::new("-")).collect::>(), |files| files.map(OsStr::new).collect::>(), ); + + let verbose = ChecksumVerbose::new(status, quiet, warn); + let opts = ChecksumOptions { binary, ignore_missing, - quiet, - status, strict, - warn, + verbose, }; // Execute the checksum validation @@ -356,14 +358,16 @@ pub fn uu_app_common() -> Command { .short('q') .long(options::QUIET) .help("don't print OK for each successfully verified file") - .action(ArgAction::SetTrue), + .action(ArgAction::SetTrue) + .overrides_with_all([options::STATUS, options::WARN]), ) .arg( Arg::new(options::STATUS) .short('s') .long("status") .help("don't output anything, status code shows success") - .action(ArgAction::SetTrue), + .action(ArgAction::SetTrue) + .overrides_with_all([options::QUIET, options::WARN]), ) .arg( Arg::new(options::STRICT) @@ -382,7 +386,8 @@ pub fn uu_app_common() -> Command { .short('w') .long("warn") .help("warn about improperly formatted checksum lines") - .action(ArgAction::SetTrue), + .action(ArgAction::SetTrue) + .overrides_with_all([options::QUIET, options::STATUS]), ) .arg( Arg::new("zero") diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index ce8f2cb95..9dacc4c5c 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -148,15 +148,57 @@ impl From for FileCheckError { } } +#[derive(Debug, PartialEq, Eq, PartialOrd, Clone, Copy)] +pub enum ChecksumVerbose { + Status, + Quiet, + Normal, + Warning, +} + +impl ChecksumVerbose { + pub fn new(status: bool, quiet: bool, warn: bool) -> Self { + use ChecksumVerbose::*; + + // Assume only one of the three booleans will be enabled at once. + // This is ensured by clap's overriding arguments. + match (status, quiet, warn) { + (true, _, _) => Status, + (_, true, _) => Quiet, + (_, _, true) => Warning, + _ => Normal, + } + } + + #[inline] + pub fn over_status(self) -> bool { + self > Self::Status + } + + #[inline] + pub fn over_quiet(self) -> bool { + self > Self::Quiet + } + + #[inline] + pub fn at_least_warning(self) -> bool { + self >= Self::Warning + } +} + +impl Default for ChecksumVerbose { + fn default() -> Self { + Self::Normal + } +} + /// This struct regroups CLI flags. #[derive(Debug, Default, Clone, Copy)] pub struct ChecksumOptions { pub binary: bool, pub ignore_missing: bool, - pub quiet: bool, - pub status: bool, pub strict: bool, - pub warn: bool, + pub verbose: ChecksumVerbose, } #[derive(Debug, Error)] @@ -235,20 +277,19 @@ pub fn create_sha3(bits: Option) -> UResult { } #[allow(clippy::comparison_chain)] -fn cksum_output(res: &ChecksumResult, status: bool) { +fn print_cksum_report(res: &ChecksumResult) { if res.bad_format == 1 { show_warning_caps!("{} line is improperly formatted", res.bad_format); } else if res.bad_format > 1 { show_warning_caps!("{} lines are improperly formatted", res.bad_format); } - if !status { - if res.failed_cksum == 1 { - show_warning_caps!("{} computed checksum did NOT match", res.failed_cksum); - } else if res.failed_cksum > 1 { - show_warning_caps!("{} computed checksums did NOT match", res.failed_cksum); - } + if res.failed_cksum == 1 { + show_warning_caps!("{} computed checksum did NOT match", res.failed_cksum); + } else if res.failed_cksum > 1 { + show_warning_caps!("{} computed checksums did NOT match", res.failed_cksum); } + if res.failed_open_file == 1 { show_warning_caps!("{} listed file could not be read", res.failed_open_file); } else if res.failed_open_file > 1 { @@ -284,10 +325,10 @@ impl FileChecksumResult { /// The cli options might prevent to display on the outcome of the /// comparison on STDOUT. - fn can_display(&self, opts: ChecksumOptions) -> bool { + fn can_display(&self, verbose: ChecksumVerbose) -> bool { match self { - FileChecksumResult::Ok => !opts.status && !opts.quiet, - FileChecksumResult::Failed => !opts.status, + FileChecksumResult::Ok => verbose.over_quiet(), + FileChecksumResult::Failed => verbose.over_status(), FileChecksumResult::CantOpen => true, } } @@ -310,9 +351,9 @@ fn print_file_report( filename: &[u8], result: FileChecksumResult, prefix: &str, - opts: ChecksumOptions, + verbose: ChecksumVerbose, ) { - if result.can_display(opts) { + if result.can_display(verbose) { let _ = write!(w, "{prefix}"); let _ = w.write_all(filename); let _ = writeln!(w, ": {result}"); @@ -589,7 +630,7 @@ fn get_file_to_check( filename_bytes, FileChecksumResult::CantOpen, "", - opts, + opts.verbose, ); }; match File::open(filename) { @@ -710,7 +751,7 @@ fn compute_and_check_digest_from_file( filename, FileChecksumResult::from_bool(checksum_correct), prefix, - opts, + opts.verbose, ); if checksum_correct { @@ -886,7 +927,7 @@ fn process_checksum_file( Err(ImproperlyFormatted) => { res.bad_format += 1; - if opts.warn { + if opts.verbose.at_least_warning() { let algo = if let Some(algo_name_input) = cli_algo_name { Cow::Owned(algo_name_input.to_uppercase()) } else if let Some(algo) = &last_algo { @@ -912,7 +953,7 @@ fn process_checksum_file( // not a single line correctly formatted found // return an error if res.total_properly_formatted() == 0 { - if !opts.status { + if opts.verbose.over_status() { log_no_properly_formatted(get_filename_for_output(filename_input, input_is_stdin)); } set_exit_code(1); @@ -920,16 +961,20 @@ fn process_checksum_file( } // if any incorrectly formatted line, show it - cksum_output(&res, opts.status); + if opts.verbose.over_status() { + print_cksum_report(&res); + } if opts.ignore_missing && res.correct == 0 { // we have only bad format // and we had ignore-missing - eprintln!( - "{}: {}: no file was verified", - util_name(), - filename_input.maybe_quote(), - ); + if opts.verbose.over_status() { + eprintln!( + "{}: {}: no file was verified", + util_name(), + filename_input.maybe_quote(), + ); + } set_exit_code(1); } @@ -1425,7 +1470,7 @@ mod tests { for (filename, result, prefix, expected) in cases { let mut buffer: Vec = vec![]; - print_file_report(&mut buffer, filename, *result, prefix, opts); + print_file_report(&mut buffer, filename, *result, prefix, opts.verbose); assert_eq!(&buffer, expected) } }