proc_exit/
exit.rs

1use std::io::Write;
2
3/// For use in `fn run() -> ExitResult {}`
4pub type ExitResult = Result<(), Exit>;
5
6/// Error type for exiting programs.
7pub struct Exit {
8    code: crate::Code,
9    msg: Option<Box<dyn std::fmt::Display>>,
10}
11
12impl Exit {
13    #[inline]
14    pub fn new(code: crate::Code) -> Self {
15        Self { code, msg: None }
16    }
17
18    #[inline]
19    pub fn with_message<D: std::fmt::Display + 'static>(mut self, msg: D) -> Self {
20        self.msg = Some(Box::new(msg));
21        self
22    }
23}
24
25impl std::fmt::Display for Exit {
26    #[inline]
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        if let Some(msg) = self.msg.as_ref() {
29            msg.fmt(f)
30        } else {
31            Ok(())
32        }
33    }
34}
35
36impl std::process::Termination for Exit {
37    #[inline]
38    fn report(self) -> std::process::ExitCode {
39        self.code
40            .as_exit_code()
41            .unwrap_or(std::process::ExitCode::FAILURE)
42    }
43}
44
45impl std::fmt::Debug for Exit {
46    #[inline]
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        // For compatibility with `std::process::Termination`
49        std::fmt::Display::fmt(self, f)
50    }
51}
52
53/// Extension for converting errors to `Exit`.
54pub trait WithCodeResultExt<T> {
55    /// Convert an Error into an `Exit`
56    fn with_code(self, code: crate::Code) -> Result<T, Exit>;
57}
58
59impl<T, E: std::fmt::Display + 'static> WithCodeResultExt<T> for Result<T, E> {
60    #[inline]
61    fn with_code(self, code: crate::Code) -> Result<T, Exit> {
62        self.map_err(|e| Exit::new(code).with_message(e))
63    }
64}
65
66/// Report any error message and exit.
67#[inline]
68pub fn exit(result: ExitResult) -> ! {
69    let code = report(result);
70    code.process_exit()
71}
72
73/// Report, delegating exiting to the caller.
74#[inline]
75pub fn report(result: ExitResult) -> crate::Code {
76    match result {
77        Ok(()) => crate::Code::SUCCESS,
78        Err(err) => {
79            if let Some(msg) = err.msg {
80                // At this point, we might be exiting due to a broken pipe, just do our best and
81                // move on.
82                let _ = writeln!(std::io::stderr(), "{}", msg);
83            }
84            err.code
85        }
86    }
87}