diff --git a/Cargo.lock b/Cargo.lock index b0f54656ca7..50012488882 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2629,6 +2629,7 @@ dependencies = [ "libc", "nix", "signal-hook", + "thiserror 2.0.12", "uucore", ] @@ -2702,6 +2703,7 @@ name = "uu_expand" version = "0.0.30" dependencies = [ "clap", + "thiserror 2.0.12", "unicode-width 0.2.0", "uucore", ] @@ -2996,6 +2998,7 @@ name = "uu_numfmt" version = "0.0.30" dependencies = [ "clap", + "thiserror 2.0.12", "uucore", ] @@ -3068,6 +3071,7 @@ version = "0.0.30" dependencies = [ "clap", "regex", + "thiserror 2.0.12", "uucore", ] @@ -3193,6 +3197,7 @@ version = "0.0.30" dependencies = [ "clap", "memchr", + "thiserror 2.0.12", "uucore", ] @@ -3260,6 +3265,7 @@ dependencies = [ "memchr", "memmap2", "regex", + "thiserror 2.0.12", "uucore", ] @@ -3377,6 +3383,7 @@ name = "uu_unexpand" version = "0.0.30" dependencies = [ "clap", + "thiserror 2.0.12", "unicode-width 0.2.0", "uucore", ] diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index 208ede22078..7ad69477869 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -21,6 +21,7 @@ clap = { workspace = true } gcd = { workspace = true } libc = { workspace = true } uucore = { workspace = true, features = ["format", "quoting-style"] } +thiserror = { workspace = true } [target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] signal-hook = { workspace = true } diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index fa441d12f09..32242004d4c 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -9,28 +9,42 @@ mod unit_tests; use super::{ConversionMode, IConvFlags, IFlags, Num, OConvFlags, OFlags, Settings, StatusLevel}; use crate::conversion_tables::ConversionTable; -use std::error::Error; +use thiserror::Error; use uucore::display::Quotable; use uucore::error::UError; use uucore::parse_size::{ParseSizeError, Parser as SizeParser}; use uucore::show_warning; /// Parser Errors describe errors with parser input -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Error)] pub enum ParseError { + #[error("Unrecognized operand '{0}'")] UnrecognizedOperand(String), + #[error("Only one of conv=ascii conv=ebcdic or conv=ibm may be specified")] MultipleFmtTable, + #[error("Only one of conv=lcase or conv=ucase may be specified")] MultipleUCaseLCase, + #[error("Only one of conv=block or conv=unblock may be specified")] MultipleBlockUnblock, + #[error("Only one ov conv=excl or conv=nocreat may be specified")] MultipleExclNoCreate, + #[error("invalid input flag: ‘{}’\nTry '{} --help' for more information.", .0, uucore::execution_phrase())] FlagNoMatch(String), + #[error("Unrecognized conv=CONV -> {0}")] ConvFlagNoMatch(String), + #[error("invalid number: ‘{0}’")] MultiplierStringParseFailure(String), + #[error("Multiplier string would overflow on current system -> {0}")] MultiplierStringOverflow(String), + #[error("conv=block or conv=unblock specified without cbs=N")] BlockUnblockWithoutCBS, + #[error("status=LEVEL not recognized -> {0}")] StatusLevelNotRecognized(String), + #[error("feature not implemented on this system -> {0}")] Unimplemented(String), + #[error("{0}=N cannot fit into memory")] BsOutOfRange(String), + #[error("invalid number: ‘{0}’")] InvalidNumber(String), } @@ -396,69 +410,6 @@ impl Parser { } } -impl std::fmt::Display for ParseError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::UnrecognizedOperand(arg) => { - write!(f, "Unrecognized operand '{arg}'") - } - Self::MultipleFmtTable => { - write!( - f, - "Only one of conv=ascii conv=ebcdic or conv=ibm may be specified" - ) - } - Self::MultipleUCaseLCase => { - write!(f, "Only one of conv=lcase or conv=ucase may be specified") - } - Self::MultipleBlockUnblock => { - write!(f, "Only one of conv=block or conv=unblock may be specified") - } - Self::MultipleExclNoCreate => { - write!(f, "Only one ov conv=excl or conv=nocreat may be specified") - } - Self::FlagNoMatch(arg) => { - // Additional message about 'dd --help' is displayed only in this situation. - write!( - f, - "invalid input flag: ‘{}’\nTry '{} --help' for more information.", - arg, - uucore::execution_phrase() - ) - } - Self::ConvFlagNoMatch(arg) => { - write!(f, "Unrecognized conv=CONV -> {arg}") - } - Self::MultiplierStringParseFailure(arg) => { - write!(f, "invalid number: ‘{arg}’") - } - Self::MultiplierStringOverflow(arg) => { - write!( - f, - "Multiplier string would overflow on current system -> {arg}" - ) - } - Self::BlockUnblockWithoutCBS => { - write!(f, "conv=block or conv=unblock specified without cbs=N") - } - Self::StatusLevelNotRecognized(arg) => { - write!(f, "status=LEVEL not recognized -> {arg}") - } - Self::BsOutOfRange(arg) => { - write!(f, "{arg}=N cannot fit into memory") - } - Self::Unimplemented(arg) => { - write!(f, "feature not implemented on this system -> {arg}") - } - Self::InvalidNumber(arg) => { - write!(f, "invalid number: ‘{arg}’") - } - } - } -} - -impl Error for ParseError {} - impl UError for ParseError { fn code(&self) -> i32 { 1 diff --git a/src/uu/expand/Cargo.toml b/src/uu/expand/Cargo.toml index 5c0a6d672b0..ad25d7dd610 100644 --- a/src/uu/expand/Cargo.toml +++ b/src/uu/expand/Cargo.toml @@ -20,6 +20,7 @@ path = "src/expand.rs" clap = { workspace = true } unicode-width = { workspace = true } uucore = { workspace = true } +thiserror = { workspace = true } [[bin]] name = "expand" diff --git a/src/uu/expand/src/expand.rs b/src/uu/expand/src/expand.rs index 3cde28ac683..47d1796de41 100644 --- a/src/uu/expand/src/expand.rs +++ b/src/uu/expand/src/expand.rs @@ -6,14 +6,13 @@ // spell-checker:ignore (ToDO) ctype cwidth iflag nbytes nspaces nums tspaces uflag Preprocess use clap::{Arg, ArgAction, ArgMatches, Command}; -use std::error::Error; use std::ffi::OsString; -use std::fmt; use std::fs::File; use std::io::{BufRead, BufReader, BufWriter, Read, Write, stdin, stdout}; use std::num::IntErrorKind; use std::path::Path; use std::str::from_utf8; +use thiserror::Error; use unicode_width::UnicodeWidthChar; use uucore::display::Quotable; use uucore::error::{FromIo, UError, UResult, set_exit_code}; @@ -61,43 +60,24 @@ fn is_digit_or_comma(c: char) -> bool { } /// Errors that can occur when parsing a `--tabs` argument. -#[derive(Debug)] +#[derive(Debug, Error)] enum ParseError { + #[error("tab size contains invalid character(s): {}", .0.quote())] InvalidCharacter(String), + #[error("{} specifier not at start of number: {}", .0.quote(), .1.quote())] SpecifierNotAtStartOfNumber(String, String), + #[error("{} specifier only allowed with the last value", .0.quote())] SpecifierOnlyAllowedWithLastValue(String), + #[error("tab size cannot be 0")] TabSizeCannotBeZero, + #[error("tab stop is too large {}", .0.quote())] TabSizeTooLarge(String), + #[error("tab sizes must be ascending")] TabSizesMustBeAscending, } -impl Error for ParseError {} impl UError for ParseError {} -impl fmt::Display for ParseError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::InvalidCharacter(s) => { - write!(f, "tab size contains invalid character(s): {}", s.quote()) - } - Self::SpecifierNotAtStartOfNumber(specifier, s) => write!( - f, - "{} specifier not at start of number: {}", - specifier.quote(), - s.quote(), - ), - Self::SpecifierOnlyAllowedWithLastValue(specifier) => write!( - f, - "{} specifier only allowed with the last value", - specifier.quote() - ), - Self::TabSizeCannotBeZero => write!(f, "tab size cannot be 0"), - Self::TabSizeTooLarge(s) => write!(f, "tab stop is too large {}", s.quote()), - Self::TabSizesMustBeAscending => write!(f, "tab sizes must be ascending"), - } - } -} - /// Parse a list of tabstops from a `--tabs` argument. /// /// This function returns both the vector of numbers appearing in the diff --git a/src/uu/numfmt/Cargo.toml b/src/uu/numfmt/Cargo.toml index e0d28590746..0bad48915d1 100644 --- a/src/uu/numfmt/Cargo.toml +++ b/src/uu/numfmt/Cargo.toml @@ -19,6 +19,7 @@ path = "src/numfmt.rs" [dependencies] clap = { workspace = true } uucore = { workspace = true, features = ["ranges"] } +thiserror = { workspace = true } [[bin]] name = "numfmt" diff --git a/src/uu/numfmt/src/errors.rs b/src/uu/numfmt/src/errors.rs index 77dd6f0aade..d3dcc48732c 100644 --- a/src/uu/numfmt/src/errors.rs +++ b/src/uu/numfmt/src/errors.rs @@ -3,13 +3,12 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use std::{ - error::Error, - fmt::{Debug, Display}, -}; +use std::fmt::Debug; +use thiserror::Error; use uucore::error::UError; -#[derive(Debug)] +#[derive(Debug, Error)] +#[error("{0}")] pub enum NumfmtError { IoError(String), IllegalArgument(String), @@ -25,15 +24,3 @@ impl UError for NumfmtError { } } } - -impl Error for NumfmtError {} - -impl Display for NumfmtError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::IoError(s) | Self::IllegalArgument(s) | Self::FormattingError(s) => { - write!(f, "{s}") - } - } - } -} diff --git a/src/uu/ptx/Cargo.toml b/src/uu/ptx/Cargo.toml index 593aee1bde9..d703a4f4058 100644 --- a/src/uu/ptx/Cargo.toml +++ b/src/uu/ptx/Cargo.toml @@ -20,6 +20,7 @@ path = "src/ptx.rs" clap = { workspace = true } regex = { workspace = true } uucore = { workspace = true } +thiserror = { workspace = true } [[bin]] name = "ptx" diff --git a/src/uu/ptx/src/ptx.rs b/src/uu/ptx/src/ptx.rs index 12686474e4c..00d4f611e04 100644 --- a/src/uu/ptx/src/ptx.rs +++ b/src/uu/ptx/src/ptx.rs @@ -9,11 +9,11 @@ use clap::{Arg, ArgAction, Command}; use regex::Regex; use std::cmp; use std::collections::{BTreeSet, HashMap, HashSet}; -use std::error::Error; -use std::fmt::{Display, Formatter, Write as FmtWrite}; +use std::fmt::Write as FmtWrite; use std::fs::File; use std::io::{BufRead, BufReader, BufWriter, Read, Write, stdin, stdout}; use std::num::ParseIntError; +use thiserror::Error; use uucore::display::Quotable; use uucore::error::{FromIo, UError, UResult, UUsageError}; use uucore::{format_usage, help_about, help_usage}; @@ -194,28 +194,18 @@ struct WordRef { filename: String, } -#[derive(Debug)] +#[derive(Debug, Error)] enum PtxError { + #[error("There is no dumb format with GNU extensions disabled")] DumbFormat, + #[error("{0} not implemented yet")] NotImplemented(&'static str), + #[error("{0}")] ParseError(ParseIntError), } -impl Error for PtxError {} impl UError for PtxError {} -impl Display for PtxError { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - match self { - Self::DumbFormat => { - write!(f, "There is no dumb format with GNU extensions disabled") - } - Self::NotImplemented(s) => write!(f, "{s} not implemented yet"), - Self::ParseError(e) => e.fmt(f), - } - } -} - fn get_config(matches: &clap::ArgMatches) -> UResult { let mut config = Config::default(); let err_msg = "parsing options failed"; diff --git a/src/uu/split/Cargo.toml b/src/uu/split/Cargo.toml index 631c455bb04..47b200bcaa4 100644 --- a/src/uu/split/Cargo.toml +++ b/src/uu/split/Cargo.toml @@ -20,6 +20,7 @@ path = "src/split.rs" clap = { workspace = true } memchr = { workspace = true } uucore = { workspace = true, features = ["fs"] } +thiserror = { workspace = true } [[bin]] name = "split" diff --git a/src/uu/split/src/filenames.rs b/src/uu/split/src/filenames.rs index d2883a711ba..52b284a167f 100644 --- a/src/uu/split/src/filenames.rs +++ b/src/uu/split/src/filenames.rs @@ -39,8 +39,8 @@ use crate::{ OPT_NUMERIC_SUFFIXES_SHORT, OPT_SUFFIX_LENGTH, }; use clap::ArgMatches; -use std::fmt; use std::path::is_separator; +use thiserror::Error; use uucore::display::Quotable; use uucore::error::{UResult, USimpleError}; @@ -79,31 +79,21 @@ pub struct Suffix { } /// An error when parsing suffix parameters from command-line arguments. +#[derive(Debug, Error)] pub enum SuffixError { /// Invalid suffix length parameter. + #[error("invalid suffix length: {}", .0.quote())] NotParsable(String), /// Suffix contains a directory separator, which is not allowed. + #[error("invalid suffix {}, contains directory separator", .0.quote())] ContainsSeparator(String), /// Suffix is not large enough to split into specified chunks + #[error("the suffix length needs to be at least {0}")] TooSmall(usize), } -impl fmt::Display for SuffixError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::NotParsable(s) => write!(f, "invalid suffix length: {}", s.quote()), - Self::TooSmall(i) => write!(f, "the suffix length needs to be at least {i}"), - Self::ContainsSeparator(s) => write!( - f, - "invalid suffix {}, contains directory separator", - s.quote() - ), - } - } -} - impl Suffix { /// Parse the suffix type, start, length and additional suffix from the command-line arguments /// as well process suffix length auto-widening and auto-width scenarios diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index b453040fb17..e7321d53315 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -15,11 +15,11 @@ use crate::strategy::{NumberType, Strategy, StrategyError}; use clap::{Arg, ArgAction, ArgMatches, Command, ValueHint, parser::ValueSource}; use std::env; use std::ffi::OsString; -use std::fmt; use std::fs::{File, metadata}; use std::io; use std::io::{BufRead, BufReader, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write, stdin}; use std::path::Path; +use thiserror::Error; use uucore::display::Quotable; use uucore::error::{FromIo, UIoError, UResult, USimpleError, UUsageError}; use uucore::parse_size::parse_size_u64; @@ -411,31 +411,39 @@ struct Settings { io_blksize: Option, } +#[derive(Debug, Error)] /// An error when parsing settings from command-line arguments. enum SettingsError { /// Invalid chunking strategy. + #[error("{0}")] Strategy(StrategyError), /// Invalid suffix length parameter. + #[error("{0}")] Suffix(SuffixError), /// Multi-character (Invalid) separator + #[error("multi-character separator {}", .0.quote())] MultiCharacterSeparator(String), /// Multiple different separator characters + #[error("multiple separator characters specified")] MultipleSeparatorCharacters, /// Using `--filter` with `--number` option sub-strategies that print Kth chunk out of N chunks to stdout /// K/N /// l/K/N /// r/K/N + #[error("--filter does not process a chunk extracted to stdout")] FilterWithKthChunkNumber, /// Invalid IO block size + #[error("invalid IO block size: {}", .0.quote())] InvalidIOBlockSize(String), /// The `--filter` option is not supported on Windows. #[cfg(windows)] + #[error("{OPT_FILTER} is currently not supported in this platform")] NotSupported, } @@ -450,30 +458,6 @@ impl SettingsError { } } -impl fmt::Display for SettingsError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Strategy(e) => e.fmt(f), - Self::Suffix(e) => e.fmt(f), - Self::MultiCharacterSeparator(s) => { - write!(f, "multi-character separator {}", s.quote()) - } - Self::MultipleSeparatorCharacters => { - write!(f, "multiple separator characters specified") - } - Self::FilterWithKthChunkNumber => { - write!(f, "--filter does not process a chunk extracted to stdout") - } - Self::InvalidIOBlockSize(s) => write!(f, "invalid IO block size: {}", s.quote()), - #[cfg(windows)] - Self::NotSupported => write!( - f, - "{OPT_FILTER} is currently not supported in this platform" - ), - } - } -} - impl Settings { /// Parse a strategy from the command-line arguments. fn from(matches: &ArgMatches, obs_lines: &Option) -> Result { diff --git a/src/uu/split/src/strategy.rs b/src/uu/split/src/strategy.rs index f8f50b09407..7c0d182a131 100644 --- a/src/uu/split/src/strategy.rs +++ b/src/uu/split/src/strategy.rs @@ -7,7 +7,7 @@ use crate::{OPT_BYTES, OPT_LINE_BYTES, OPT_LINES, OPT_NUMBER}; use clap::{ArgMatches, parser::ValueSource}; -use std::fmt; +use thiserror::Error; use uucore::{ display::Quotable, parse_size::{ParseSizeError, parse_size_u64, parse_size_u64_max}, @@ -54,7 +54,7 @@ impl NumberType { } /// An error due to an invalid parameter to the `-n` command-line option. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Error)] pub enum NumberTypeError { /// The number of chunks was invalid. /// @@ -69,6 +69,7 @@ pub enum NumberTypeError { /// -n r/N /// -n r/K/N /// ``` + #[error("invalid number of chunks: {}", .0.quote())] NumberOfChunks(String), /// The chunk number was invalid. @@ -83,6 +84,7 @@ pub enum NumberTypeError { /// -n l/K/N /// -n r/K/N /// ``` + #[error("invalid chunk number: {}", .0.quote())] ChunkNumber(String), } @@ -191,36 +193,25 @@ pub enum Strategy { } /// An error when parsing a chunking strategy from command-line arguments. +#[derive(Debug, Error)] pub enum StrategyError { /// Invalid number of lines. + #[error("invalid number of lines: {0}")] Lines(ParseSizeError), /// Invalid number of bytes. + #[error("invalid number of bytes: {0}")] Bytes(ParseSizeError), /// Invalid number type. + #[error("{0}")] NumberType(NumberTypeError), /// Multiple chunking strategies were specified (but only one should be). + #[error("cannot split in more than one way")] MultipleWays, } -impl fmt::Display for StrategyError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Lines(e) => write!(f, "invalid number of lines: {e}"), - Self::Bytes(e) => write!(f, "invalid number of bytes: {e}"), - Self::NumberType(NumberTypeError::NumberOfChunks(s)) => { - write!(f, "invalid number of chunks: {}", s.quote()) - } - Self::NumberType(NumberTypeError::ChunkNumber(s)) => { - write!(f, "invalid chunk number: {}", s.quote()) - } - Self::MultipleWays => write!(f, "cannot split in more than one way"), - } - } -} - impl Strategy { /// Parse a strategy from the command-line arguments. pub fn from(matches: &ArgMatches, obs_lines: &Option) -> Result { diff --git a/src/uu/tac/Cargo.toml b/src/uu/tac/Cargo.toml index 3d1fb6c5d02..e6ded1a4ac4 100644 --- a/src/uu/tac/Cargo.toml +++ b/src/uu/tac/Cargo.toml @@ -24,6 +24,7 @@ memmap2 = { workspace = true } regex = { workspace = true } clap = { workspace = true } uucore = { workspace = true } +thiserror = { workspace = true } [[bin]] name = "tac" diff --git a/src/uu/tac/src/error.rs b/src/uu/tac/src/error.rs index 7a737ad9b97..fc01bbffd2c 100644 --- a/src/uu/tac/src/error.rs +++ b/src/uu/tac/src/error.rs @@ -3,33 +3,37 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. //! Errors returned by tac during processing of a file. -use std::error::Error; -use std::fmt::Display; +use thiserror::Error; use uucore::display::Quotable; use uucore::error::UError; -#[derive(Debug)] +#[derive(Debug, Error)] pub enum TacError { /// A regular expression given by the user is invalid. + #[error("invalid regular expression: {0}")] InvalidRegex(regex::Error), /// An argument to tac is invalid. + #[error("{}: read error: Invalid argument", _0.maybe_quote())] InvalidArgument(String), /// The specified file is not found on the filesystem. + #[error("failed to open {} for reading: No such file or directory", _0.quote())] FileNotFound(String), /// An error reading the contents of a file or stdin. /// /// The parameters are the name of the file and the underlying /// [`std::io::Error`] that caused this error. + #[error("failed to read from {0}: {1}")] ReadError(String, std::io::Error), /// An error writing the (reversed) contents of a file or stdin. /// /// The parameter is the underlying [`std::io::Error`] that caused /// this error. + #[error("failed to write to stdout: {0}")] WriteError(std::io::Error), } @@ -38,23 +42,3 @@ impl UError for TacError { 1 } } - -impl Error for TacError {} - -impl Display for TacError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::InvalidRegex(e) => write!(f, "invalid regular expression: {e}"), - Self::InvalidArgument(s) => { - write!(f, "{}: read error: Invalid argument", s.maybe_quote()) - } - Self::FileNotFound(s) => write!( - f, - "failed to open {} for reading: No such file or directory", - s.quote() - ), - Self::ReadError(s, e) => write!(f, "failed to read from {s}: {e}"), - Self::WriteError(e) => write!(f, "failed to write to stdout: {e}"), - } - } -} diff --git a/src/uu/unexpand/Cargo.toml b/src/uu/unexpand/Cargo.toml index a096e7b6c46..79248a70f55 100644 --- a/src/uu/unexpand/Cargo.toml +++ b/src/uu/unexpand/Cargo.toml @@ -17,6 +17,7 @@ readme.workspace = true path = "src/unexpand.rs" [dependencies] +thiserror = { workspace = true } clap = { workspace = true } unicode-width = { workspace = true } uucore = { workspace = true } diff --git a/src/uu/unexpand/src/unexpand.rs b/src/uu/unexpand/src/unexpand.rs index 07abe456f91..a0d2db47c62 100644 --- a/src/uu/unexpand/src/unexpand.rs +++ b/src/uu/unexpand/src/unexpand.rs @@ -6,13 +6,12 @@ // spell-checker:ignore (ToDO) nums aflag uflag scol prevtab amode ctype cwidth nbytes lastcol pctype Preprocess use clap::{Arg, ArgAction, Command}; -use std::error::Error; -use std::fmt; use std::fs::File; use std::io::{BufRead, BufReader, BufWriter, Read, Stdout, Write, stdin, stdout}; use std::num::IntErrorKind; use std::path::Path; use std::str::from_utf8; +use thiserror::Error; use unicode_width::UnicodeWidthChar; use uucore::display::Quotable; use uucore::error::{FromIo, UError, UResult, USimpleError}; @@ -23,30 +22,20 @@ const ABOUT: &str = help_about!("unexpand.md"); const DEFAULT_TABSTOP: usize = 8; -#[derive(Debug)] +#[derive(Debug, Error)] enum ParseError { + #[error("tab size contains invalid character(s): {}", _0.quote())] InvalidCharacter(String), + #[error("tab size cannot be 0")] TabSizeCannotBeZero, + #[error("tab stop value is too large")] TabSizeTooLarge, + #[error("tab sizes must be ascending")] TabSizesMustBeAscending, } -impl Error for ParseError {} impl UError for ParseError {} -impl fmt::Display for ParseError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::InvalidCharacter(s) => { - write!(f, "tab size contains invalid character(s): {}", s.quote()) - } - Self::TabSizeCannotBeZero => write!(f, "tab size cannot be 0"), - Self::TabSizeTooLarge => write!(f, "tab stop value is too large"), - Self::TabSizesMustBeAscending => write!(f, "tab sizes must be ascending"), - } - } -} - fn tabstops_parse(s: &str) -> Result, ParseError> { let words = s.split(','); diff --git a/src/uu/wc/src/utf8/read.rs b/src/uu/wc/src/utf8/read.rs index 3556436f96b..af10cbb5366 100644 --- a/src/uu/wc/src/utf8/read.rs +++ b/src/uu/wc/src/utf8/read.rs @@ -4,9 +4,8 @@ // file that was distributed with this source code. // spell-checker:ignore bytestream use super::*; -use std::error::Error; -use std::fmt; use std::io::{self, BufRead}; +use thiserror::Error; /// Wraps a `std::io::BufRead` buffered byte stream and decode it as UTF-8. pub struct BufReadDecoder { @@ -15,36 +14,18 @@ pub struct BufReadDecoder { incomplete: Incomplete, } -#[derive(Debug)] +#[derive(Debug, Error)] pub enum BufReadDecoderError<'a> { /// Represents one UTF-8 error in the byte stream. /// /// In lossy decoding, each such error should be replaced with U+FFFD. /// (See `BufReadDecoder::next_lossy` and `BufReadDecoderError::lossy`.) + #[error("invalid byte sequence: {0:02x?}")] InvalidByteSequence(&'a [u8]), /// An I/O error from the underlying byte stream - Io(io::Error), -} - -impl fmt::Display for BufReadDecoderError<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - BufReadDecoderError::InvalidByteSequence(bytes) => { - write!(f, "invalid byte sequence: {bytes:02x?}") - } - BufReadDecoderError::Io(ref err) => write!(f, "underlying bytestream error: {err}"), - } - } -} - -impl Error for BufReadDecoderError<'_> { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match *self { - BufReadDecoderError::InvalidByteSequence(_) => None, - BufReadDecoderError::Io(ref err) => Some(err), - } - } + #[error("underlying bytestream error: {0}")] + Io(#[source] io::Error), } impl BufReadDecoder {