diff --git a/Cargo.lock b/Cargo.lock index b02f7eaa905..fdd92de4e2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2678,6 +2678,7 @@ dependencies = [ "chrono", "clap", "glob", + "thiserror 2.0.11", "uucore", "windows-sys 0.59.0", ] @@ -2764,6 +2765,7 @@ name = "uu_groups" version = "0.0.29" dependencies = [ "clap", + "thiserror 2.0.11", "uucore", ] @@ -2922,6 +2924,7 @@ dependencies = [ "clap", "rand", "tempfile", + "thiserror 2.0.11", "uucore", ] @@ -2973,6 +2976,7 @@ version = "0.0.29" dependencies = [ "clap", "libc", + "thiserror 2.0.11", "uucore", ] @@ -3346,6 +3350,7 @@ name = "uu_tsort" version = "0.0.29" dependencies = [ "clap", + "thiserror 2.0.11", "uucore", ] diff --git a/src/uu/du/Cargo.toml b/src/uu/du/Cargo.toml index 27ec1700a5f..0c0f2aaad07 100644 --- a/src/uu/du/Cargo.toml +++ b/src/uu/du/Cargo.toml @@ -22,6 +22,7 @@ chrono = { workspace = true } glob = { workspace = true } clap = { workspace = true } uucore = { workspace = true, features = ["format"] } +thiserror = { workspace = true } [target.'cfg(target_os = "windows")'.dependencies] windows-sys = { workspace = true, features = [ diff --git a/src/uu/du/src/du.rs b/src/uu/du/src/du.rs index bd017f1d515..5b9017f3171 100644 --- a/src/uu/du/src/du.rs +++ b/src/uu/du/src/du.rs @@ -8,8 +8,6 @@ use clap::{builder::PossibleValue, crate_version, Arg, ArgAction, ArgMatches, Co use glob::Pattern; use std::collections::HashSet; use std::env; -use std::error::Error; -use std::fmt::Display; #[cfg(not(windows))] use std::fs::Metadata; use std::fs::{self, DirEntry, File}; @@ -25,6 +23,7 @@ use std::str::FromStr; use std::sync::mpsc; use std::thread; use std::time::{Duration, UNIX_EPOCH}; +use thiserror::Error; use uucore::display::{print_verbatim, Quotable}; use uucore::error::{set_exit_code, FromIo, UError, UResult, USimpleError}; use uucore::line_ending::LineEnding; @@ -409,48 +408,26 @@ fn du( Ok(my_stat) } -#[derive(Debug)] +#[derive(Debug, Error)] enum DuError { + #[error("invalid maximum depth {depth}", depth = .0.quote())] InvalidMaxDepthArg(String), + + #[error("summarizing conflicts with --max-depth={depth}", depth = .0.maybe_quote())] SummarizeDepthConflict(String), + + #[error("invalid argument {style} for 'time style'\nValid arguments are:\n- 'full-iso'\n- 'long-iso'\n- 'iso'\nTry '{help}' for more information.", + style = .0.quote(), + help = uucore::execution_phrase())] InvalidTimeStyleArg(String), + + #[error("'birth' and 'creation' arguments for --time are not supported on this platform.")] InvalidTimeArg, - InvalidGlob(String), -} -impl Display for DuError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::InvalidMaxDepthArg(s) => write!(f, "invalid maximum depth {}", s.quote()), - Self::SummarizeDepthConflict(s) => { - write!( - f, - "summarizing conflicts with --max-depth={}", - s.maybe_quote() - ) - } - Self::InvalidTimeStyleArg(s) => write!( - f, - "invalid argument {} for 'time style' -Valid arguments are: -- 'full-iso' -- 'long-iso' -- 'iso' -Try '{} --help' for more information.", - s.quote(), - uucore::execution_phrase() - ), - Self::InvalidTimeArg => write!( - f, - "'birth' and 'creation' arguments for --time are not supported on this platform.", - ), - Self::InvalidGlob(s) => write!(f, "Invalid exclude syntax: {s}"), - } - } + #[error("Invalid exclude syntax: {0}")] + InvalidGlob(String), } -impl Error for DuError {} - impl UError for DuError { fn code(&self) -> i32 { match self { diff --git a/src/uu/groups/Cargo.toml b/src/uu/groups/Cargo.toml index 11055f529a3..51074ea2cab 100644 --- a/src/uu/groups/Cargo.toml +++ b/src/uu/groups/Cargo.toml @@ -18,6 +18,7 @@ path = "src/groups.rs" [dependencies] clap = { workspace = true } +thiserror = { workspace = true } uucore = { workspace = true, features = ["entries", "process"] } [[bin]] diff --git a/src/uu/groups/src/groups.rs b/src/uu/groups/src/groups.rs index 0f0dfce804a..46c9a224515 100644 --- a/src/uu/groups/src/groups.rs +++ b/src/uu/groups/src/groups.rs @@ -2,18 +2,10 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// -// ============================================================================ -// Test suite summary for GNU coreutils 8.32.162-4eda -// ============================================================================ -// PASS: tests/misc/groups-dash.sh -// PASS: tests/misc/groups-process-all.sh -// PASS: tests/misc/groups-version.sh // spell-checker:ignore (ToDO) passwd -use std::error::Error; -use std::fmt::Display; +use thiserror::Error; use uucore::{ display::Quotable, entries::{get_groups_gnu, gid2grp, Locate, Passwd}, @@ -29,26 +21,20 @@ mod options { const ABOUT: &str = help_about!("groups.md"); const USAGE: &str = help_usage!("groups.md"); -#[derive(Debug)] +#[derive(Debug, Error)] enum GroupsError { + #[error("failed to fetch groups")] GetGroupsFailed, + + #[error("cannot find name for group ID {0}")] GroupNotFound(u32), + + #[error("{user}: no such user", user = .0.quote())] UserNotFound(String), } -impl Error for GroupsError {} impl UError for GroupsError {} -impl Display for GroupsError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Self::GetGroupsFailed => write!(f, "failed to fetch groups"), - Self::GroupNotFound(gid) => write!(f, "cannot find name for group ID {gid}"), - Self::UserNotFound(user) => write!(f, "{}: no such user", user.quote()), - } - } -} - fn infallible_gid2grp(gid: &u32) -> String { match gid2grp(*gid) { Ok(grp) => grp, diff --git a/src/uu/mktemp/Cargo.toml b/src/uu/mktemp/Cargo.toml index 60d0d28997b..3d7cfa04faf 100644 --- a/src/uu/mktemp/Cargo.toml +++ b/src/uu/mktemp/Cargo.toml @@ -20,6 +20,7 @@ path = "src/mktemp.rs" clap = { workspace = true } rand = { workspace = true } tempfile = { workspace = true } +thiserror = { workspace = true } uucore = { workspace = true } [[bin]] diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index 00f23c50adc..f00ee2d7286 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -11,9 +11,7 @@ use uucore::error::{FromIo, UError, UResult, UUsageError}; use uucore::{format_usage, help_about, help_usage}; use std::env; -use std::error::Error; use std::ffi::OsStr; -use std::fmt::Display; use std::io::ErrorKind; use std::iter; use std::path::{Path, PathBuf, MAIN_SEPARATOR}; @@ -25,6 +23,7 @@ use std::os::unix::prelude::PermissionsExt; use rand::Rng; use tempfile::Builder; +use thiserror::Error; const ABOUT: &str = help_about!("mktemp.md"); const USAGE: &str = help_usage!("mktemp.md"); @@ -46,21 +45,35 @@ const TMPDIR_ENV_VAR: &str = "TMPDIR"; #[cfg(windows)] const TMPDIR_ENV_VAR: &str = "TMP"; -#[derive(Debug)] +#[derive(Debug, Error)] enum MkTempError { + #[error("could not persist file {path}", path = .0.quote())] PersistError(PathBuf), + + #[error("with --suffix, template {template} must end in X", template = .0.quote())] MustEndInX(String), + + #[error("too few X's in template {template}", template = .0.quote())] TooFewXs(String), /// The template prefix contains a path separator (e.g. `"a/bXXX"`). + #[error("invalid template, {template}, contains directory separator", template = .0.quote())] PrefixContainsDirSeparator(String), /// The template suffix contains a path separator (e.g. `"XXXa/b"`). + #[error("invalid suffix {suffix}, contains directory separator", suffix = .0.quote())] SuffixContainsDirSeparator(String), + + #[error("invalid template, {template}; with --tmpdir, it may not be absolute", template = .0.quote())] InvalidTemplate(String), + + #[error("too many templates")] TooManyTemplates, /// When a specified temporary directory could not be found. + #[error("failed to create {template_type} via template {template}: No such file or directory", + template_type = .0, + template = .1.quote())] NotFound(String, String), } @@ -70,47 +83,6 @@ impl UError for MkTempError { } } -impl Error for MkTempError {} - -impl Display for MkTempError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use MkTempError::*; - match self { - PersistError(p) => write!(f, "could not persist file {}", p.quote()), - MustEndInX(s) => write!(f, "with --suffix, template {} must end in X", s.quote()), - TooFewXs(s) => write!(f, "too few X's in template {}", s.quote()), - PrefixContainsDirSeparator(s) => { - write!( - f, - "invalid template, {}, contains directory separator", - s.quote() - ) - } - SuffixContainsDirSeparator(s) => { - write!( - f, - "invalid suffix {}, contains directory separator", - s.quote() - ) - } - InvalidTemplate(s) => write!( - f, - "invalid template, {}; with --tmpdir, it may not be absolute", - s.quote() - ), - TooManyTemplates => { - write!(f, "too many templates") - } - NotFound(template_type, s) => write!( - f, - "failed to create {} via template {}: No such file or directory", - template_type, - s.quote() - ), - } - } -} - /// Options parsed from the command-line. /// /// This provides a layer of indirection between the application logic diff --git a/src/uu/nohup/Cargo.toml b/src/uu/nohup/Cargo.toml index df324856107..0ca725e6c59 100644 --- a/src/uu/nohup/Cargo.toml +++ b/src/uu/nohup/Cargo.toml @@ -20,6 +20,7 @@ path = "src/nohup.rs" clap = { workspace = true } libc = { workspace = true } uucore = { workspace = true, features = ["fs"] } +thiserror = { workspace = true } [[bin]] name = "nohup" diff --git a/src/uu/nohup/src/nohup.rs b/src/uu/nohup/src/nohup.rs index 60ad979bbfe..00643d48837 100644 --- a/src/uu/nohup/src/nohup.rs +++ b/src/uu/nohup/src/nohup.rs @@ -10,11 +10,11 @@ use libc::{c_char, dup2, execvp, signal}; use libc::{SIGHUP, SIG_IGN}; use std::env; use std::ffi::CString; -use std::fmt::{Display, Formatter}; use std::fs::{File, OpenOptions}; use std::io::{Error, IsTerminal}; use std::os::unix::prelude::*; use std::path::{Path, PathBuf}; +use thiserror::Error; use uucore::display::Quotable; use uucore::error::{set_exit_code, UClapError, UError, UResult}; use uucore::{format_usage, help_about, help_section, help_usage, show_error}; @@ -33,15 +33,24 @@ mod options { pub const CMD: &str = "cmd"; } -#[derive(Debug)] +#[derive(Debug, Error)] enum NohupError { + #[error("Cannot detach from console")] CannotDetach, - CannotReplace(&'static str, std::io::Error), - OpenFailed(i32, std::io::Error), - OpenFailed2(i32, std::io::Error, String, std::io::Error), -} -impl std::error::Error for NohupError {} + #[error("Cannot replace {name}: {err}", name = .0, err = .1)] + CannotReplace(&'static str, #[source] Error), + + #[error("failed to open {path}: {err}", path = NOHUP_OUT.quote(), err = .1)] + OpenFailed(i32, #[source] Error), + + #[error("failed to open {first_path}: {first_err}\nfailed to open {second_path}: {second_err}", + first_path = NOHUP_OUT.quote(), + first_err = .1, + second_path = .2.quote(), + second_err = .3)] + OpenFailed2(i32, #[source] Error, String, Error), +} impl UError for NohupError { fn code(&self) -> i32 { @@ -52,26 +61,6 @@ impl UError for NohupError { } } -impl Display for NohupError { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - match self { - Self::CannotDetach => write!(f, "Cannot detach from console"), - Self::CannotReplace(s, e) => write!(f, "Cannot replace {s}: {e}"), - Self::OpenFailed(_, e) => { - write!(f, "failed to open {}: {}", NOHUP_OUT.quote(), e) - } - Self::OpenFailed2(_, e1, s, e2) => write!( - f, - "failed to open {}: {}\nfailed to open {}: {}", - NOHUP_OUT.quote(), - e1, - s.quote(), - e2 - ), - } - } -} - #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(args).with_exit_code(125)?; diff --git a/src/uu/tsort/Cargo.toml b/src/uu/tsort/Cargo.toml index ba53e0d7be8..77c2686a4b5 100644 --- a/src/uu/tsort/Cargo.toml +++ b/src/uu/tsort/Cargo.toml @@ -18,6 +18,7 @@ path = "src/tsort.rs" [dependencies] clap = { workspace = true } +thiserror = { workspace = true } uucore = { workspace = true } [[bin]] diff --git a/src/uu/tsort/src/tsort.rs b/src/uu/tsort/src/tsort.rs index aac0a055fea..c051ff36451 100644 --- a/src/uu/tsort/src/tsort.rs +++ b/src/uu/tsort/src/tsort.rs @@ -5,8 +5,8 @@ //spell-checker:ignore TAOCP indegree use clap::{crate_version, Arg, Command}; use std::collections::{HashMap, HashSet, VecDeque}; -use std::fmt::Display; use std::path::Path; +use thiserror::Error; use uucore::display::Quotable; use uucore::error::{UError, UResult}; use uucore::{format_usage, help_about, help_usage, show}; @@ -18,43 +18,30 @@ mod options { pub const FILE: &str = "file"; } -#[derive(Debug)] +#[derive(Debug, Error)] enum TsortError { /// The input file is actually a directory. + #[error("{0}: read error: Is a directory")] IsDir(String), /// The number of tokens in the input data is odd. /// /// The list of edges must be even because each edge has two /// components: a source node and a target node. + #[error("{input}: input contains an odd number of tokens", input = .0.maybe_quote())] NumTokensOdd(String), /// The graph contains a cycle. + #[error("{0}: input contains a loop:")] Loop(String), /// A particular node in a cycle. (This is mainly used for printing.) + #[error("{0}")] LoopNode(String), } -impl std::error::Error for TsortError {} - impl UError for TsortError {} -impl Display for TsortError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Self::IsDir(d) => write!(f, "{d}: read error: Is a directory"), - Self::NumTokensOdd(i) => write!( - f, - "{}: input contains an odd number of tokens", - i.maybe_quote() - ), - Self::Loop(i) => write!(f, "{i}: input contains a loop:"), - Self::LoopNode(v) => write!(f, "{v}"), - } - } -} - #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(args)?;