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
use clap::{crate_version, value_parser, Arg, ArgAction, Command};
use std::error::Error;
use std::ffi::OsStr;
use std::fmt::Display;
use std::fs::File;
use std::io::{self, stdin, stdout, BufReader, Read, Write};
use std::iter;
use std::path::Path;
use uucore::checksum::{
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,
};
use uucore::{
encoding,
error::{FromIo, UError, UResult, USimpleError},
error::{FromIo, UResult, USimpleError},
format_usage, help_about, help_section, help_usage, show,
sum::{div_ceil, Digest},
};
@ -28,11 +26,6 @@ const USAGE: &str = help_usage!("cksum.md");
const ABOUT: &str = help_about!("cksum.md");
const AFTER_HELP: &str = help_section!("after help", "cksum.md");
#[derive(Debug)]
enum CkSumError {
RawMultipleFiles,
}
#[derive(Debug, PartialEq)]
enum OutputFormat {
Hexadecimal,
@ -40,26 +33,6 @@ enum OutputFormat {
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 {
algo_name: &'static str,
digest: Box<dyn Digest + 'static>,
@ -83,7 +56,7 @@ where
{
let files: Vec<_> = files.collect();
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 {
@ -287,11 +260,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
};
if ["bsd", "crc", "sysv"].contains(&algo_name) && check {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"--check is not supported with --algorithm={bsd,sysv,crc}",
)
.into());
return Err(ChecksumError::AlgorithmNotSupportedWithCheck.into());
}
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 {
calculate_blake2b_length(*length)?
} else {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"--length is only supported with --algorithm=blake2b",
)
.into());
return Err(ChecksumError::LengthOnlyForBlake2b.into());
}
}
None => None,
@ -320,11 +285,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let ignore_missing = matches.get_flag(options::IGNORE_MISSING);
if (binary_flag || text_flag) && check {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"the --binary and --text options are meaningless when verifying checksums",
)
.into());
return Err(ChecksumError::BinaryTextConflict.into());
}
// Determine the appropriate algorithm option to pass
let algo_option = if algo_name.is_empty() {

View file

@ -10,10 +10,9 @@ use clap::crate_version;
use clap::value_parser;
use clap::ArgAction;
use clap::{Arg, ArgMatches, Command};
use std::error::Error;
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::io::{self, stdin, BufReader, Read};
use std::io::{stdin, BufReader, Read};
use std::iter;
use std::num::ParseIntError;
use std::path::Path;
@ -23,10 +22,10 @@ use uucore::checksum::detect_algo;
use uucore::checksum::digest_reader;
use uucore::checksum::escape_filename;
use uucore::checksum::perform_checksum_validation;
use uucore::checksum::ChecksumError;
use uucore::checksum::HashAlgorithm;
use uucore::checksum::ALGORITHM_OPTIONS_BLAKE2B;
use uucore::error::USimpleError;
use uucore::error::{FromIo, UError, UResult};
use uucore::error::{FromIo, UResult};
use uucore::sum::{Digest, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
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<()> {
if alg.is_some() {
return Err(USimpleError::new(
1,
"You cannot combine multiple hash algorithms!",
));
return Err(ChecksumError::CombineMultipleAlgorithms.into());
}
alg = Some(new_alg);
Ok(())
@ -139,7 +135,7 @@ fn create_algorithm_from_flags(matches: &ArgMatches) -> UResult<HashAlgorithm> {
create_fn: Box::new(|| Box::new(Shake128::new())),
bits: *bits,
})?,
None => return Err(USimpleError::new(1, "--bits required for SHAKE128")),
None => return Err(ChecksumError::BitsRequiredForShake128.into()),
};
}
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())),
bits: *bits,
})?,
None => return Err(USimpleError::new(1, "--bits required for SHAKE256")),
None => return Err(ChecksumError::BitsRequiredForShake256.into()),
};
}
if alg.is_none() {
return Err(USimpleError::new(
1,
"Needs an algorithm to hash with.\nUse --help for more information.",
));
return Err(ChecksumError::NeedAlgoToHash.into());
}
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" {
calculate_blake2b_length(*length)?
} else {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"--length is only supported with --algorithm=blake2b",
)
.into());
return Err(ChecksumError::LengthOnlyForBlake2b.into());
}
}
None => None,
@ -239,7 +228,7 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
if ignore_missing && !check {
// --ignore-missing needs -c
return Err(HashsumError::IgnoreNotCheck.into());
return Err(ChecksumError::IgnoreNotCheck.into());
}
let opts = Options {
@ -264,11 +253,7 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
let strict = matches.get_flag("strict");
if (binary_flag || text_flag) && check {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"the --binary and --text options are meaningless when verifying checksums",
)
.into());
return Err(ChecksumError::BinaryTextConflict.into());
}
// Determine the appropriate algorithm option to pass
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)]
fn hashsum<'a, I>(mut options: Options, files: I) -> UResult<()>
where

View file

@ -6,14 +6,16 @@
use os_display::Quotable;
use regex::Regex;
use std::{
error::Error,
ffi::OsStr,
fmt::Display,
fs::File,
io::{self, BufReader, Read},
path::Path,
};
use crate::{
error::{set_exit_code, FromIo, UResult, USimpleError},
error::{set_exit_code, FromIo, UError, UResult, USimpleError},
show, show_error, show_warning_caps,
sum::{
Blake2b, Blake3, Digest, DigestWriter, Md5, Sha1, Sha224, Sha256, Sha384, Sha3_224,
@ -65,6 +67,74 @@ pub struct HashAlgorithm {
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.
///
/// # Returns
@ -95,11 +165,8 @@ pub fn create_sha3(bits: Option<usize>) -> UResult<HashAlgorithm> {
bits: 512,
}),
Some(_) => Err(USimpleError::new(
1,
"Invalid output size for SHA3 (expected 224, 256, 384, or 512)",
)),
None => Err(USimpleError::new(1, "--bits required for SHA3")),
Some(_) => Err(ChecksumError::InvalidOutputSizeForSha3.into()),
None => Err(ChecksumError::BitsRequiredForSha3.into()),
}
}
@ -228,10 +295,7 @@ pub fn detect_algo(algo: &str, length: Option<usize>) -> UResult<HashAlgorithm>
//ALGORITHM_OPTIONS_SHA3 | "sha3" => (
alg if alg.starts_with("sha3") => create_sha3(length),
_ => Err(USimpleError::new(
1,
"unknown algorithm: clap should have prevented this case",
)),
_ => Err(ChecksumError::UnknownAlgorithm.into()),
}
}

View file

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

View file

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