Skip to main content

uucore/mods/
error.rs

1// This file is part of the uutils coreutils package.
2//
3// For the full copyright and license information, please view the LICENSE
4// file that was distributed with this source code.
5//! All utils return exit with an exit code. Usually, the following scheme is used:
6//! * `0`: succeeded
7//! * `1`: minor problems
8//! * `2`: major problems
9//!
10//! This module provides types to reconcile these exit codes with idiomatic Rust error
11//! handling. This has a couple advantages over manually using [`std::process::exit`]:
12//! 1. It enables the use of `?`, `map_err`, `unwrap_or`, etc. in `uumain`.
13//! 1. It encourages the use of [`UResult`]/[`Result`] in functions in the utils.
14//! 1. The error messages are largely standardized across utils.
15//! 1. Standardized error messages can be created from external result types
16//!    (i.e. [`std::io::Result`] & `clap::ClapResult`).
17//! 1. [`set_exit_code`] takes away the burden of manually tracking exit codes for non-fatal errors.
18//!
19//! # Usage
20//! The signature of a typical util should be:
21//! ```ignore
22//! fn uumain(args: impl uucore::Args) -> UResult<()> {
23//!     ...
24//! }
25//! ```
26//! [`UResult`] is a simple wrapper around [`Result`] with a custom error trait: [`UError`]. The
27//! most important difference with types implementing [`std::error::Error`] is that [`UError`]s
28//! can specify the exit code of the program when they are returned from `uumain`:
29//! * When `Ok` is returned, the code set with [`set_exit_code`] is used as exit code. If
30//!   [`set_exit_code`] was not used, then `0` is used.
31//! * When `Err` is returned, the code corresponding with the error is used as exit code and the
32//!   error message is displayed.
33//!
34//! Additionally, the errors can be displayed manually with the [`crate::show`] and [`crate::show_if_err`] macros:
35//! ```ignore
36//! let res = Err(USimpleError::new(1, "Error!!"));
37//! show_if_err!(res);
38//! // or
39//! if let Err(e) = res {
40//!    show!(e);
41//! }
42//! ```
43//!
44//! **Note**: The [`crate::show`] and [`crate::show_if_err`] macros set the exit code of the program using
45//! [`set_exit_code`]. See the documentation on that function for more information.
46//!
47//! # Guidelines
48//! * Use error types from `uucore` where possible.
49//! * Add error types to `uucore` if an error appears in multiple utils.
50//! * Prefer proper custom error types over [`ExitCode`] and [`USimpleError`].
51//! * [`USimpleError`] may be used in small utils with simple error handling.
52//! * Using [`ExitCode`] is not recommended but can be useful for converting utils to use
53//!   [`UResult`].
54
55// spell-checker:ignore uioerror rustdoc
56
57use std::{
58    cell::Cell,
59    error::Error,
60    fmt::{Display, Formatter},
61    io::Write,
62    sync::atomic::{AtomicI32, Ordering},
63};
64
65static EXIT_CODE: AtomicI32 = AtomicI32::new(0);
66
67/// Get the last exit code set with [`set_exit_code`].
68/// The default value is `0`.
69pub fn get_exit_code() -> i32 {
70    EXIT_CODE.load(Ordering::SeqCst)
71}
72
73/// Set the exit code for the program if `uumain` returns `Ok(())`.
74///
75/// This function is most useful for non-fatal errors, for example when applying an operation to
76/// multiple files:
77/// ```ignore
78/// use uucore::error::{UResult, set_exit_code};
79///
80/// fn uumain(args: impl uucore::Args) -> UResult<()> {
81///     ...
82///     for file in files {
83///         let res = some_operation_that_might_fail(file);
84///         match res {
85///             Ok() => {},
86///             Err(_) => set_exit_code(1),
87///         }
88///     }
89///     Ok(()) // If any of the operations failed, 1 is returned.
90/// }
91/// ```
92pub fn set_exit_code(code: i32) {
93    EXIT_CODE.store(code, Ordering::SeqCst);
94}
95
96/// Result type that should be returned by all utils.
97pub type UResult<T> = Result<T, Box<dyn UError>>;
98
99/// Custom errors defined by the utils and `uucore`.
100///
101/// All errors should implement [`std::error::Error`], [`std::fmt::Display`] and
102/// [`std::fmt::Debug`] and have an additional `code` method that specifies the
103/// exit code of the program if the error is returned from `uumain`.
104///
105/// An example of a custom error from `ls`:
106///
107/// ```
108/// use uucore::{
109///     display::Quotable,
110///     error::{UError, UResult}
111/// };
112/// use std::{
113///     error::Error,
114///     fmt::{Display, Debug},
115///     path::PathBuf
116/// };
117///
118/// #[derive(Debug)]
119/// enum LsError {
120///     InvalidLineWidth(String),
121///     NoMetadata(PathBuf),
122/// }
123///
124/// impl UError for LsError {
125///     fn code(&self) -> i32 {
126///         match self {
127///             LsError::InvalidLineWidth(_) => 2,
128///             LsError::NoMetadata(_) => 1,
129///         }
130///     }
131/// }
132///
133/// impl Error for LsError {}
134///
135/// impl Display for LsError {
136///     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137///         match self {
138///             LsError::InvalidLineWidth(s) => write!(f, "invalid line width: {}", s.quote()),
139///             LsError::NoMetadata(p) => write!(f, "could not open file: {}", p.quote()),
140///         }
141///     }
142/// }
143/// ```
144///
145/// The main routine would look like this:
146///
147/// ```ignore
148/// #[uucore::main]
149/// pub fn uumain(args: impl uucore::Args) -> UResult<()> {
150///     // Perform computations here ...
151///     return Err(LsError::InvalidLineWidth(String::from("test")).into())
152/// }
153/// ```
154///
155/// The call to `into()` is required to convert the `LsError` to
156/// [`Box<dyn UError>`]. The implementation for `From` is provided automatically.
157///
158/// A crate like [`quick_error`](https://crates.io/crates/quick-error) might
159/// also be used, but will still require an `impl` for the `code` method.
160pub trait UError: Error + Send {
161    /// Error code of a custom error.
162    ///
163    /// Set a return value for each variant of an enum-type to associate an
164    /// error code (which is returned to the system shell) with an error
165    /// variant.
166    ///
167    /// # Example
168    ///
169    /// ```
170    /// use uucore::{
171    ///     display::Quotable,
172    ///     error::UError
173    /// };
174    /// use std::{
175    ///     error::Error,
176    ///     fmt::{Display, Debug},
177    ///     path::PathBuf
178    /// };
179    ///
180    /// #[derive(Debug)]
181    /// enum MyError {
182    ///     Foo(String),
183    ///     Bar(PathBuf),
184    ///     Bing(),
185    /// }
186    ///
187    /// impl UError for MyError {
188    ///     fn code(&self) -> i32 {
189    ///         match self {
190    ///             MyError::Foo(_) => 2,
191    ///             // All other errors yield the same error code, there's no
192    ///             // need to list them explicitly.
193    ///             _ => 1,
194    ///         }
195    ///     }
196    /// }
197    ///
198    /// impl Error for MyError {}
199    ///
200    /// impl Display for MyError {
201    ///     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202    ///         use MyError as ME;
203    ///         match self {
204    ///             ME::Foo(s) => write!(f, "Unknown Foo: {}", s.quote()),
205    ///             ME::Bar(p) => write!(f, "Couldn't find Bar: {}", p.quote()),
206    ///             ME::Bing() => write!(f, "Exterminate!"),
207    ///         }
208    ///     }
209    /// }
210    /// ```
211    fn code(&self) -> i32 {
212        1
213    }
214
215    /// Print usage help to a custom error.
216    ///
217    /// Return true or false to control whether a short usage help is printed
218    /// below the error message. The usage help is in the format: "Try `{name}
219    /// --help` for more information." and printed only if `true` is returned.
220    ///
221    /// # Example
222    ///
223    /// ```
224    /// use uucore::{
225    ///     display::Quotable,
226    ///     error::UError
227    /// };
228    /// use std::{
229    ///     error::Error,
230    ///     fmt::{Display, Debug},
231    ///     path::PathBuf
232    /// };
233    ///
234    /// #[derive(Debug)]
235    /// enum MyError {
236    ///     Foo(String),
237    ///     Bar(PathBuf),
238    ///     Bing(),
239    /// }
240    ///
241    /// impl UError for MyError {
242    ///     fn usage(&self) -> bool {
243    ///         match self {
244    ///             // This will have a short usage help appended
245    ///             MyError::Bar(_) => true,
246    ///             // These matches won't have a short usage help appended
247    ///             _ => false,
248    ///         }
249    ///     }
250    /// }
251    ///
252    /// impl Error for MyError {}
253    ///
254    /// impl Display for MyError {
255    ///     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256    ///         use MyError as ME;
257    ///         match self {
258    ///             ME::Foo(s) => write!(f, "Unknown Foo: {}", s.quote()),
259    ///             ME::Bar(p) => write!(f, "Couldn't find Bar: {}", p.quote()),
260    ///             ME::Bing() => write!(f, "Exterminate!"),
261    ///         }
262    ///     }
263    /// }
264    /// ```
265    fn usage(&self) -> bool {
266        false
267    }
268}
269
270impl<T> From<T> for Box<dyn UError>
271where
272    T: UError + 'static,
273{
274    fn from(t: T) -> Self {
275        Box::new(t)
276    }
277}
278
279/// A simple error type with an exit code and a message that implements [`UError`].
280///
281/// ```
282/// use uucore::error::{UResult, USimpleError};
283/// let err = USimpleError { code: 1, message: "error!".into()};
284/// let res: UResult<()> = Err(err.into());
285/// // or using the `new` method:
286/// let res: UResult<()> = Err(USimpleError::new(1, "error!"));
287/// ```
288#[derive(Debug)]
289pub struct USimpleError {
290    /// Exit code of the error.
291    pub code: i32,
292
293    /// Error message.
294    pub message: String,
295}
296
297impl USimpleError {
298    /// Create a new `USimpleError` with a given exit code and message.
299    #[allow(clippy::new_ret_no_self)]
300    pub fn new<S: Into<String>>(code: i32, message: S) -> Box<dyn UError> {
301        Box::new(Self {
302            code,
303            message: message.into(),
304        })
305    }
306}
307
308impl Error for USimpleError {}
309
310impl Display for USimpleError {
311    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
312        self.message.fmt(f)
313    }
314}
315
316impl UError for USimpleError {
317    fn code(&self) -> i32 {
318        self.code
319    }
320}
321
322/// Wrapper type around [`std::io::Error`].
323#[derive(Debug)]
324pub struct UUsageError {
325    /// Exit code of the error.
326    pub code: i32,
327
328    /// Error message.
329    pub message: String,
330}
331
332impl UUsageError {
333    #[allow(clippy::new_ret_no_self)]
334    /// Create a new `UUsageError` with a given exit code and message.
335    pub fn new<S: Into<String>>(code: i32, message: S) -> Box<dyn UError> {
336        Box::new(Self {
337            code,
338            message: message.into(),
339        })
340    }
341}
342
343impl Error for UUsageError {}
344
345impl Display for UUsageError {
346    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
347        self.message.fmt(f)
348    }
349}
350
351impl UError for UUsageError {
352    fn code(&self) -> i32 {
353        self.code
354    }
355
356    fn usage(&self) -> bool {
357        true
358    }
359}
360
361/// Wrapper type around [`std::io::Error`].
362///
363/// The messages displayed by [`UIoError`] should match the error messages displayed by GNU
364/// coreutils.
365///
366/// There are two ways to construct this type: with [`UIoError::new`] or by calling the
367/// [`FromIo::map_err_context`] method on a [`std::io::Result`] or [`std::io::Error`].
368/// ```
369/// use uucore::{
370///     display::Quotable,
371///     error::{FromIo, UResult, UIoError, UError}
372/// };
373/// use std::fs::File;
374/// use std::path::Path;
375/// let path = Path::new("test.txt");
376///
377/// // Manual construction
378/// let e: Box<dyn UError> = UIoError::new(
379///     std::io::ErrorKind::NotFound,
380///     format!("cannot access {}", path.quote())
381/// );
382/// let res: UResult<()> = Err(e.into());
383///
384/// // Converting from an `std::io::Error`.
385/// let res: UResult<File> = File::open(path).map_err_context(|| format!("cannot access {}", path.quote()));
386/// ```
387#[derive(Debug)]
388pub struct UIoError {
389    context: Option<String>,
390    inner: std::io::Error,
391}
392
393impl UIoError {
394    #[allow(clippy::new_ret_no_self)]
395    /// Create a new `UIoError` with a given exit code and message.
396    pub fn new<S: Into<String>>(kind: std::io::ErrorKind, context: S) -> Box<dyn UError> {
397        Box::new(Self {
398            context: Some(context.into()),
399            inner: kind.into(),
400        })
401    }
402}
403
404impl UError for UIoError {}
405
406impl Error for UIoError {}
407
408impl Display for UIoError {
409    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
410        use std::io::ErrorKind::*;
411
412        let message;
413        let message = if self.inner.raw_os_error().is_some() {
414            // These are errors that come directly from the OS.
415            // We want to normalize their messages across systems,
416            // and we want to strip the "(os error X)" suffix.
417            match self.inner.kind() {
418                NotFound => "No such file or directory",
419                PermissionDenied => "Permission denied",
420                ConnectionRefused => "Connection refused",
421                ConnectionReset => "Connection reset",
422                ConnectionAborted => "Connection aborted",
423                NotConnected => "Not connected",
424                AddrInUse => "Address in use",
425                AddrNotAvailable => "Address not available",
426                BrokenPipe => "Broken pipe",
427                AlreadyExists => "Already exists",
428                WouldBlock => "Would block",
429                InvalidInput => "Invalid input",
430                InvalidData => "Invalid data",
431                TimedOut => "Timed out",
432                WriteZero => "Write zero",
433                Interrupted => "Interrupted",
434                UnexpectedEof => "Unexpected end of file",
435                _ => {
436                    // TODO: When the new error variants
437                    // (https://github.com/rust-lang/rust/issues/86442)
438                    // are stabilized, we should add them to the match statement.
439                    message = strip_errno(&self.inner);
440                    &message
441                }
442            }
443        } else {
444            // These messages don't need as much normalization, and the above
445            // messages wouldn't always be a good substitute.
446            // For example, ErrorKind::NotFound doesn't necessarily mean it was
447            // a file that was not found.
448            // There are also errors with entirely custom messages.
449            message = self.inner.to_string();
450            &message
451        };
452        if let Some(ctx) = &self.context {
453            write!(f, "{ctx}: {message}")
454        } else {
455            write!(f, "{message}")
456        }
457    }
458}
459
460/// Strip the trailing " (os error XX)" from io error strings.
461pub fn strip_errno(err: &std::io::Error) -> String {
462    let mut msg = err.to_string();
463    if let Some(pos) = msg.find(" (os error ") {
464        msg.truncate(pos);
465    }
466    msg
467}
468
469/// Enables the conversion from [`std::io::Error`] to [`UError`] and from [`std::io::Result`] to
470/// [`UResult`].
471pub trait FromIo<T> {
472    /// Map the error context of an [`std::io::Error`] or [`std::io::Result`] to a custom error
473    fn map_err_context(self, context: impl FnOnce() -> String) -> T;
474}
475
476impl FromIo<Box<UIoError>> for std::io::Error {
477    fn map_err_context(self, context: impl FnOnce() -> String) -> Box<UIoError> {
478        Box::new(UIoError {
479            context: Some(context()),
480            inner: self,
481        })
482    }
483}
484
485impl<T> FromIo<UResult<T>> for std::io::Result<T> {
486    fn map_err_context(self, context: impl FnOnce() -> String) -> UResult<T> {
487        self.map_err(|e| e.map_err_context(context) as Box<dyn UError>)
488    }
489}
490
491impl FromIo<Box<UIoError>> for std::io::ErrorKind {
492    fn map_err_context(self, context: impl FnOnce() -> String) -> Box<UIoError> {
493        Box::new(UIoError {
494            context: Some(context()),
495            inner: std::io::Error::new(self, ""),
496        })
497    }
498}
499
500impl From<std::io::Error> for UIoError {
501    fn from(f: std::io::Error) -> Self {
502        Self {
503            context: None,
504            inner: f,
505        }
506    }
507}
508
509impl From<std::io::Error> for Box<dyn UError> {
510    fn from(f: std::io::Error) -> Self {
511        let u_error: UIoError = f.into();
512        Box::new(u_error) as Self
513    }
514}
515
516/// Enables the conversion from [`Result<T, nix::Error>`] to [`UResult<T>`].
517///
518/// # Examples
519///
520/// ```
521/// use uucore::error::FromIo;
522/// use nix::errno::Errno;
523///
524/// let nix_err = Err::<(), nix::Error>(Errno::EACCES);
525/// let uio_result = nix_err.map_err_context(|| String::from("fix me please!"));
526///
527/// // prints "fix me please!: Permission denied"
528/// println!("{}", uio_result.unwrap_err());
529/// ```
530#[cfg(unix)]
531impl<T> FromIo<UResult<T>> for Result<T, nix::Error> {
532    fn map_err_context(self, context: impl FnOnce() -> String) -> UResult<T> {
533        self.map_err(|e| {
534            Box::new(UIoError {
535                context: Some(context()),
536                inner: std::io::Error::from_raw_os_error(e as i32),
537            }) as Box<dyn UError>
538        })
539    }
540}
541
542#[cfg(unix)]
543impl<T> FromIo<UResult<T>> for nix::Error {
544    fn map_err_context(self, context: impl FnOnce() -> String) -> UResult<T> {
545        Err(Box::new(UIoError {
546            context: Some(context()),
547            inner: std::io::Error::from_raw_os_error(self as i32),
548        }) as Box<dyn UError>)
549    }
550}
551
552#[cfg(unix)]
553impl From<nix::Error> for UIoError {
554    fn from(f: nix::Error) -> Self {
555        Self {
556            context: None,
557            inner: std::io::Error::from_raw_os_error(f as i32),
558        }
559    }
560}
561
562#[cfg(unix)]
563impl From<nix::Error> for Box<dyn UError> {
564    fn from(f: nix::Error) -> Self {
565        let u_error: UIoError = f.into();
566        Box::new(u_error) as Self
567    }
568}
569
570/// Shorthand to construct [`UIoError`]-instances.
571///
572/// This macro serves as a convenience call to quickly construct instances of
573/// [`UIoError`]. It takes:
574///
575/// - An instance of [`std::io::Error`]
576/// - A `format!`-compatible string and
577/// - An arbitrary number of arguments to the format string
578///
579/// In exactly this order. It is equivalent to the more verbose code seen in the
580/// example.
581///
582/// # Examples
583///
584/// ```
585/// use uucore::error::UIoError;
586/// use uucore::uio_error;
587///
588/// let io_err = std::io::Error::new(
589///     std::io::ErrorKind::PermissionDenied, "fix me please!"
590/// );
591///
592/// let uio_err = UIoError::new(
593///     io_err.kind(),
594///     format!("Error code: {}", 2)
595/// );
596///
597/// let other_uio_err = uio_error!(io_err, "Error code: {}", 2);
598///
599/// // prints "fix me please!: Permission denied"
600/// println!("{uio_err}");
601/// // prints "Error code: 2: Permission denied"
602/// println!("{other_uio_err}");
603/// ```
604///
605/// The [`std::fmt::Display`] impl of [`UIoError`] will then ensure that an
606/// appropriate error message relating to the actual error kind of the
607/// [`std::io::Error`] is appended to whatever error message is defined in
608/// addition (as secondary argument).
609///
610/// If you want to show only the error message for the [`std::io::ErrorKind`]
611/// that's contained in [`UIoError`], pass the second argument as empty string:
612///
613/// ```
614/// use uucore::error::UIoError;
615/// use uucore::uio_error;
616///
617/// let io_err = std::io::Error::new(
618///     std::io::ErrorKind::PermissionDenied, "fix me please!"
619/// );
620///
621/// let other_uio_err = uio_error!(io_err, "");
622///
623/// // prints: ": Permission denied"
624/// println!("{other_uio_err}");
625/// ```
626//#[macro_use]
627#[macro_export]
628macro_rules! uio_error(
629    ($err:expr, $($args:tt)+) => ({
630        UIoError::new(
631            $err.kind(),
632            format!($($args)+)
633        )
634    })
635);
636
637/// A special error type that does not print any message when returned from
638/// `uumain`. Especially useful for porting utilities to using [`UResult`].
639///
640/// There are two ways to construct an [`ExitCode`]:
641/// ```
642/// use uucore::error::{ExitCode, UResult};
643/// // Explicit
644/// let res: UResult<()> = Err(ExitCode(1).into());
645///
646/// // Using into on `i32`:
647/// let res: UResult<()> = Err(1.into());
648/// ```
649/// This type is especially useful for a trivial conversion from utils returning [`i32`] to
650/// returning [`UResult`].
651#[derive(Debug)]
652pub struct ExitCode(pub i32);
653
654impl ExitCode {
655    #[allow(clippy::new_ret_no_self)]
656    /// Create a new `ExitCode` with a given exit code.
657    pub fn new(code: i32) -> Box<dyn UError> {
658        Box::new(Self(code))
659    }
660}
661
662impl Error for ExitCode {}
663
664impl Display for ExitCode {
665    fn fmt(&self, _: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
666        Ok(())
667    }
668}
669
670impl UError for ExitCode {
671    fn code(&self) -> i32 {
672        self.0
673    }
674}
675
676impl From<i32> for Box<dyn UError> {
677    fn from(i: i32) -> Self {
678        ExitCode::new(i)
679    }
680}
681
682/// A wrapper for `clap::Error` that implements [`UError`]
683///
684/// Contains a custom error code. When `Display::fmt` is called on this struct
685/// the [`clap::Error`] will be printed _directly to `stdout` or `stderr`_.
686/// This is because `clap` only supports colored output when it prints directly.
687///
688/// [`ClapErrorWrapper`] is generally created by calling the
689/// [`UClapError::with_exit_code`] method on [`clap::Error`] or using the [`From`]
690/// implementation from [`clap::Error`] to `Box<dyn UError>`, which constructs
691/// a [`ClapErrorWrapper`] with an exit code of `1`.
692///
693/// ```rust
694/// use uucore::error::{ClapErrorWrapper, UError, UClapError};
695/// let command = clap::Command::new("test");
696/// let result: Result<_, ClapErrorWrapper> = command.try_get_matches().with_exit_code(125);
697///
698/// let command = clap::Command::new("test");
699/// let result: Result<_, Box<dyn UError>> = command.try_get_matches().map_err(Into::into);
700/// ```
701#[derive(Debug)]
702pub struct ClapErrorWrapper {
703    code: i32,
704    error: clap::Error,
705    print_failed: Cell<bool>,
706}
707
708/// Extension trait for `clap::Error` to adjust the exit code.
709pub trait UClapError<T> {
710    /// Set the exit code for the program if `uumain` returns `Ok(())`.
711    fn with_exit_code(self, code: i32) -> T;
712}
713
714impl From<clap::Error> for Box<dyn UError> {
715    fn from(e: clap::Error) -> Self {
716        Box::new(ClapErrorWrapper {
717            code: 1,
718            error: e,
719            print_failed: Cell::new(false),
720        })
721    }
722}
723
724impl UClapError<ClapErrorWrapper> for clap::Error {
725    fn with_exit_code(self, code: i32) -> ClapErrorWrapper {
726        ClapErrorWrapper {
727            code,
728            error: self,
729            print_failed: Cell::new(false),
730        }
731    }
732}
733
734impl UClapError<Result<clap::ArgMatches, ClapErrorWrapper>>
735    for Result<clap::ArgMatches, clap::Error>
736{
737    fn with_exit_code(self, code: i32) -> Result<clap::ArgMatches, ClapErrorWrapper> {
738        self.map_err(|e| e.with_exit_code(code))
739    }
740}
741
742impl UError for ClapErrorWrapper {
743    fn code(&self) -> i32 {
744        // If the error is a DisplayHelp or DisplayVersion variant,
745        // check if printing failed. If it did, return 1, otherwise 0.
746        if let clap::error::ErrorKind::DisplayHelp | clap::error::ErrorKind::DisplayVersion =
747            self.error.kind()
748        {
749            i32::from(self.print_failed.get())
750        } else {
751            self.code
752        }
753    }
754}
755
756impl Error for ClapErrorWrapper {}
757
758// This is abuse of the Display trait
759impl Display for ClapErrorWrapper {
760    fn fmt(&self, _f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
761        // Check if printing succeeds. For DisplayHelp and DisplayVersion,
762        // error.print() writes to stdout, so we need to detect write failures
763        // (e.g., when stdout is /dev/full).
764        if let Err(print_fail) = self.error.print() {
765            // Mark that printing failed so code() can return the appropriate exit code
766            self.print_failed.set(true);
767            // Try to display this error to stderr, but ignore if that fails too
768            // since we're already in an error state.
769            let _ = writeln!(std::io::stderr(), "{}: {print_fail}", crate::util_name());
770            // Mirror GNU behavior: when failing to print help or version, exit with error code.
771            // This avoids silent failures when stdout is full or closed.
772            set_exit_code(1);
773        }
774        // Always return Ok(()) to satisfy Display's contract and prevent panic
775        Ok(())
776    }
777}
778
779#[cfg(test)]
780mod tests {
781    #[test]
782    #[cfg(unix)]
783    fn test_nix_error_conversion() {
784        use super::{FromIo, UIoError};
785        use nix::errno::Errno;
786        use std::io::ErrorKind;
787
788        for (nix_error, expected_error_kind) in [
789            (Errno::EACCES, ErrorKind::PermissionDenied),
790            (Errno::ENOENT, ErrorKind::NotFound),
791            (Errno::EEXIST, ErrorKind::AlreadyExists),
792        ] {
793            let error = UIoError::from(nix_error);
794            assert_eq!(expected_error_kind, error.inner.kind());
795        }
796        assert_eq!(
797            "test: Permission denied",
798            Err::<(), nix::Error>(Errno::EACCES)
799                .map_err_context(|| String::from("test"))
800                .unwrap_err()
801                .to_string()
802        );
803    }
804}