1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

cksum/hashsum: factor the error structure and use it more

This commit is contained in:
Sylvestre Ledru 2024-05-25 10:04:40 +02:00
parent dbe7a20e08
commit 0882eea07c
5 changed files with 93 additions and 106 deletions

View file

@ -5,21 +5,19 @@
// spell-checker:ignore (ToDO) fname, algo // spell-checker:ignore (ToDO) fname, algo
use clap::{crate_version, value_parser, Arg, ArgAction, Command}; use clap::{crate_version, value_parser, Arg, ArgAction, Command};
use std::error::Error;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fmt::Display;
use std::fs::File; use std::fs::File;
use std::io::{self, stdin, stdout, BufReader, Read, Write}; use std::io::{self, stdin, stdout, BufReader, Read, Write};
use std::iter; use std::iter;
use std::path::Path; use std::path::Path;
use uucore::checksum::{ use uucore::checksum::{
calculate_blake2b_length, detect_algo, digest_reader, perform_checksum_validation, calculate_blake2b_length, detect_algo, digest_reader, perform_checksum_validation,
ALGORITHM_OPTIONS_BLAKE2B, ALGORITHM_OPTIONS_BSD, ALGORITHM_OPTIONS_CRC, ChecksumError, ALGORITHM_OPTIONS_BLAKE2B, ALGORITHM_OPTIONS_BSD, ALGORITHM_OPTIONS_CRC,
ALGORITHM_OPTIONS_SYSV, SUPPORTED_ALGO, ALGORITHM_OPTIONS_SYSV, SUPPORTED_ALGO,
}; };
use uucore::{ use uucore::{
encoding, encoding,
error::{FromIo, UError, UResult, USimpleError}, error::{FromIo, UResult, USimpleError},
format_usage, help_about, help_section, help_usage, show, format_usage, help_about, help_section, help_usage, show,
sum::{div_ceil, Digest}, sum::{div_ceil, Digest},
}; };
@ -28,11 +26,6 @@ const USAGE: &str = help_usage!("cksum.md");
const ABOUT: &str = help_about!("cksum.md"); const ABOUT: &str = help_about!("cksum.md");
const AFTER_HELP: &str = help_section!("after help", "cksum.md"); const AFTER_HELP: &str = help_section!("after help", "cksum.md");
#[derive(Debug)]
enum CkSumError {
RawMultipleFiles,
}
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
enum OutputFormat { enum OutputFormat {
Hexadecimal, Hexadecimal,
@ -40,26 +33,6 @@ enum OutputFormat {
Base64, Base64,
} }
impl UError for CkSumError {
fn code(&self) -> i32 {
match self {
Self::RawMultipleFiles => 1,
}
}
}
impl Error for CkSumError {}
impl Display for CkSumError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::RawMultipleFiles => {
write!(f, "the --raw option is not supported with multiple files")
}
}
}
}
struct Options { struct Options {
algo_name: &'static str, algo_name: &'static str,
digest: Box<dyn Digest + 'static>, digest: Box<dyn Digest + 'static>,
@ -83,7 +56,7 @@ where
{ {
let files: Vec<_> = files.collect(); let files: Vec<_> = files.collect();
if options.output_format == OutputFormat::Raw && files.len() > 1 { if options.output_format == OutputFormat::Raw && files.len() > 1 {
return Err(Box::new(CkSumError::RawMultipleFiles)); return Err(Box::new(ChecksumError::RawMultipleFiles));
} }
for filename in files { for filename in files {
@ -287,11 +260,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
}; };
if ["bsd", "crc", "sysv"].contains(&algo_name) && check { if ["bsd", "crc", "sysv"].contains(&algo_name) && check {
return Err(io::Error::new( return Err(ChecksumError::AlgorithmNotSupportedWithCheck.into());
io::ErrorKind::InvalidInput,
"--check is not supported with --algorithm={bsd,sysv,crc}",
)
.into());
} }
let input_length = matches.get_one::<usize>(options::LENGTH); let input_length = matches.get_one::<usize>(options::LENGTH);
@ -301,11 +270,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if algo_name == ALGORITHM_OPTIONS_BLAKE2B { if algo_name == ALGORITHM_OPTIONS_BLAKE2B {
calculate_blake2b_length(*length)? calculate_blake2b_length(*length)?
} else { } else {
return Err(io::Error::new( return Err(ChecksumError::LengthOnlyForBlake2b.into());
io::ErrorKind::InvalidInput,
"--length is only supported with --algorithm=blake2b",
)
.into());
} }
} }
None => None, None => None,
@ -320,11 +285,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let ignore_missing = matches.get_flag(options::IGNORE_MISSING); let ignore_missing = matches.get_flag(options::IGNORE_MISSING);
if (binary_flag || text_flag) && check { if (binary_flag || text_flag) && check {
return Err(io::Error::new( return Err(ChecksumError::BinaryTextConflict.into());
io::ErrorKind::InvalidInput,
"the --binary and --text options are meaningless when verifying checksums",
)
.into());
} }
// Determine the appropriate algorithm option to pass // Determine the appropriate algorithm option to pass
let algo_option = if algo_name.is_empty() { let algo_option = if algo_name.is_empty() {

View file

@ -10,10 +10,9 @@ use clap::crate_version;
use clap::value_parser; use clap::value_parser;
use clap::ArgAction; use clap::ArgAction;
use clap::{Arg, ArgMatches, Command}; use clap::{Arg, ArgMatches, Command};
use std::error::Error;
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};
use std::fs::File; use std::fs::File;
use std::io::{self, stdin, BufReader, Read}; use std::io::{stdin, BufReader, Read};
use std::iter; use std::iter;
use std::num::ParseIntError; use std::num::ParseIntError;
use std::path::Path; use std::path::Path;
@ -23,10 +22,10 @@ use uucore::checksum::detect_algo;
use uucore::checksum::digest_reader; use uucore::checksum::digest_reader;
use uucore::checksum::escape_filename; use uucore::checksum::escape_filename;
use uucore::checksum::perform_checksum_validation; use uucore::checksum::perform_checksum_validation;
use uucore::checksum::ChecksumError;
use uucore::checksum::HashAlgorithm; use uucore::checksum::HashAlgorithm;
use uucore::checksum::ALGORITHM_OPTIONS_BLAKE2B; use uucore::checksum::ALGORITHM_OPTIONS_BLAKE2B;
use uucore::error::USimpleError; use uucore::error::{FromIo, UResult};
use uucore::error::{FromIo, UError, UResult};
use uucore::sum::{Digest, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256}; use uucore::sum::{Digest, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
use uucore::{format_usage, help_about, help_usage}; use uucore::{format_usage, help_about, help_usage};
@ -67,10 +66,7 @@ fn create_algorithm_from_flags(matches: &ArgMatches) -> UResult<HashAlgorithm> {
let mut set_or_err = |new_alg: HashAlgorithm| -> UResult<()> { let mut set_or_err = |new_alg: HashAlgorithm| -> UResult<()> {
if alg.is_some() { if alg.is_some() {
return Err(USimpleError::new( return Err(ChecksumError::CombineMultipleAlgorithms.into());
1,
"You cannot combine multiple hash algorithms!",
));
} }
alg = Some(new_alg); alg = Some(new_alg);
Ok(()) Ok(())
@ -139,7 +135,7 @@ fn create_algorithm_from_flags(matches: &ArgMatches) -> UResult<HashAlgorithm> {
create_fn: Box::new(|| Box::new(Shake128::new())), create_fn: Box::new(|| Box::new(Shake128::new())),
bits: *bits, bits: *bits,
})?, })?,
None => return Err(USimpleError::new(1, "--bits required for SHAKE128")), None => return Err(ChecksumError::BitsRequiredForShake128.into()),
}; };
} }
if matches.get_flag("shake256") { if matches.get_flag("shake256") {
@ -149,15 +145,12 @@ fn create_algorithm_from_flags(matches: &ArgMatches) -> UResult<HashAlgorithm> {
create_fn: Box::new(|| Box::new(Shake256::new())), create_fn: Box::new(|| Box::new(Shake256::new())),
bits: *bits, bits: *bits,
})?, })?,
None => return Err(USimpleError::new(1, "--bits required for SHAKE256")), None => return Err(ChecksumError::BitsRequiredForShake256.into()),
}; };
} }
if alg.is_none() { if alg.is_none() {
return Err(USimpleError::new( return Err(ChecksumError::NeedAlgoToHash.into());
1,
"Needs an algorithm to hash with.\nUse --help for more information.",
));
} }
Ok(alg.unwrap()) Ok(alg.unwrap())
@ -201,11 +194,7 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
if binary_name == ALGORITHM_OPTIONS_BLAKE2B || binary_name == "b2sum" { if binary_name == ALGORITHM_OPTIONS_BLAKE2B || binary_name == "b2sum" {
calculate_blake2b_length(*length)? calculate_blake2b_length(*length)?
} else { } else {
return Err(io::Error::new( return Err(ChecksumError::LengthOnlyForBlake2b.into());
io::ErrorKind::InvalidInput,
"--length is only supported with --algorithm=blake2b",
)
.into());
} }
} }
None => None, None => None,
@ -239,7 +228,7 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
if ignore_missing && !check { if ignore_missing && !check {
// --ignore-missing needs -c // --ignore-missing needs -c
return Err(HashsumError::IgnoreNotCheck.into()); return Err(ChecksumError::IgnoreNotCheck.into());
} }
let opts = Options { let opts = Options {
@ -264,11 +253,7 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
let strict = matches.get_flag("strict"); let strict = matches.get_flag("strict");
if (binary_flag || text_flag) && check { if (binary_flag || text_flag) && check {
return Err(io::Error::new( return Err(ChecksumError::BinaryTextConflict.into());
io::ErrorKind::InvalidInput,
"the --binary and --text options are meaningless when verifying checksums",
)
.into());
} }
// Determine the appropriate algorithm option to pass // Determine the appropriate algorithm option to pass
let algo_option = if algo.name.is_empty() { let algo_option = if algo.name.is_empty() {
@ -524,27 +509,6 @@ fn uu_app(binary_name: &str) -> (Command, bool) {
} }
} }
#[derive(Debug)]
enum HashsumError {
//InvalidRegex,
IgnoreNotCheck,
}
impl Error for HashsumError {}
impl UError for HashsumError {}
impl std::fmt::Display for HashsumError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
//Self::InvalidRegex => write!(f, "invalid regular expression"),
Self::IgnoreNotCheck => write!(
f,
"the --ignore-missing option is meaningful only when verifying checksums"
),
}
}
}
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
fn hashsum<'a, I>(mut options: Options, files: I) -> UResult<()> fn hashsum<'a, I>(mut options: Options, files: I) -> UResult<()>
where where

View file

@ -6,14 +6,16 @@
use os_display::Quotable; use os_display::Quotable;
use regex::Regex; use regex::Regex;
use std::{ use std::{
error::Error,
ffi::OsStr, ffi::OsStr,
fmt::Display,
fs::File, fs::File,
io::{self, BufReader, Read}, io::{self, BufReader, Read},
path::Path, path::Path,
}; };
use crate::{ use crate::{
error::{set_exit_code, FromIo, UResult, USimpleError}, error::{set_exit_code, FromIo, UError, UResult, USimpleError},
show, show_error, show_warning_caps, show, show_error, show_warning_caps,
sum::{ sum::{
Blake2b, Blake3, Digest, DigestWriter, Md5, Sha1, Sha224, Sha256, Sha384, Sha3_224, Blake2b, Blake3, Digest, DigestWriter, Md5, Sha1, Sha224, Sha256, Sha384, Sha3_224,
@ -65,6 +67,74 @@ pub struct HashAlgorithm {
pub bits: usize, pub bits: usize,
} }
#[derive(Debug)]
pub enum ChecksumError {
RawMultipleFiles,
IgnoreNotCheck,
InvalidOutputSizeForSha3,
BitsRequiredForSha3,
BitsRequiredForShake128,
BitsRequiredForShake256,
UnknownAlgorithm,
InvalidLength,
LengthOnlyForBlake2b,
BinaryTextConflict,
AlgorithmNotSupportedWithCheck,
CombineMultipleAlgorithms,
NeedAlgoToHash,
}
impl Error for ChecksumError {}
impl UError for ChecksumError {
fn code(&self) -> i32 {
1
}
}
impl Display for ChecksumError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::RawMultipleFiles => {
write!(f, "the --raw option is not supported with multiple files")
}
Self::IgnoreNotCheck => write!(
f,
"the --ignore-missing option is meaningful only when verifying checksums"
),
Self::InvalidOutputSizeForSha3 => write!(
f,
"Invalid output size for SHA3 (expected 224, 256, 384, or 512)"
),
Self::BitsRequiredForSha3 => write!(f, "--bits required for SHA3"),
Self::BitsRequiredForShake128 => write!(f, "--bits required for SHAKE128"),
Self::BitsRequiredForShake256 => write!(f, "--bits required for SHAKE256"),
Self::UnknownAlgorithm => {
write!(f, "unknown algorithm: clap should have prevented this case")
}
Self::InvalidLength => write!(f, "length is not a multiple of 8"),
Self::LengthOnlyForBlake2b => {
write!(f, "--length is only supported with --algorithm=blake2b")
}
Self::BinaryTextConflict => write!(
f,
"the --binary and --text options are meaningless when verifying checksums"
),
Self::AlgorithmNotSupportedWithCheck => write!(
f,
"--check is not supported with --algorithm={{bsd,sysv,crc}}"
),
Self::CombineMultipleAlgorithms => {
write!(f, "You cannot combine multiple hash algorithms!")
}
Self::NeedAlgoToHash => write!(
f,
"Needs an algorithm to hash with.\nUse --help for more information."
),
}
}
}
/// Creates a SHA3 hasher instance based on the specified bits argument. /// Creates a SHA3 hasher instance based on the specified bits argument.
/// ///
/// # Returns /// # Returns
@ -95,11 +165,8 @@ pub fn create_sha3(bits: Option<usize>) -> UResult<HashAlgorithm> {
bits: 512, bits: 512,
}), }),
Some(_) => Err(USimpleError::new( Some(_) => Err(ChecksumError::InvalidOutputSizeForSha3.into()),
1, None => Err(ChecksumError::BitsRequiredForSha3.into()),
"Invalid output size for SHA3 (expected 224, 256, 384, or 512)",
)),
None => Err(USimpleError::new(1, "--bits required for SHA3")),
} }
} }
@ -228,10 +295,7 @@ pub fn detect_algo(algo: &str, length: Option<usize>) -> UResult<HashAlgorithm>
//ALGORITHM_OPTIONS_SHA3 | "sha3" => ( //ALGORITHM_OPTIONS_SHA3 | "sha3" => (
alg if alg.starts_with("sha3") => create_sha3(length), alg if alg.starts_with("sha3") => create_sha3(length),
_ => Err(USimpleError::new( _ => Err(ChecksumError::UnknownAlgorithm.into()),
1,
"unknown algorithm: clap should have prevented this case",
)),
} }
} }

View file

@ -561,8 +561,7 @@ fn test_blake2b_512() {
.arg("--check") .arg("--check")
.arg("checksum") .arg("checksum")
.succeeds() .succeeds()
.stdout_contains("") .no_output();
.stderr_contains("");
} }
#[test] #[test]

View file

@ -370,7 +370,7 @@ fn test_check_md5sum_not_enough_space() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());
let at = &scene.fixtures; let at = &scene.fixtures;
for f in &["a", " b"] { for f in ["a", "b"] {
at.write(f, &format!("{f}\n")); at.write(f, &format!("{f}\n"));
} }
at.write( at.write(
@ -384,8 +384,7 @@ fn test_check_md5sum_not_enough_space() {
.arg("-c") .arg("-c")
.arg("check.md5sum") .arg("check.md5sum")
.fails() .fails()
.stdout_is("") .stderr_only("md5sum: check.md5sum: no properly formatted checksum lines found\nmd5sum: WARNING: 2 lines are improperly formatted\n");
.stderr_is("md5sum: check.md5sum: no properly formatted checksum lines found\nmd5sum: WARNING: 2 lines are improperly formatted\n");
} }
#[test] #[test]