1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! Support for [sysexits](https://www.freebsd.org/cgi/man.cgi?query=sysexits) codes
//!
//! Note: [FreeBSD no longer encourages these](https://reviews.freebsd.org/D27176)

/// Extension for converting errors to [`Exit`][crate::Exit]
///
/// # Example
///
/// ```
/// use proc_exit::prelude::*;
///
/// fn main() {
///     // Simple but Macro-less `main`
///     // - Fast compiles
///     // - Composable with other features
///     let result = run();
///     proc_exit::exit(result);
/// }
///
/// fn run() -> proc_exit::ExitResult {
///     // Integrates directly with `std::io::Error`, returning the right exit code.
///     let exit_status = std::process::Command::new("true")
///          .status().to_sysexits()?;
///     // Can pass `Command` exit codes right up, when appropriate
///     proc_exit::Code::from_status(exit_status).ok()?;
///
///     proc_exit::Code::SUCCESS.ok()
/// }
/// ```
pub trait ToSysexitsResultExt<T> {
    /// Convert an Error into an [`Exit`][crate::Exit]
    fn to_sysexits(self) -> Result<T, crate::Exit>;
}

impl<T> ToSysexitsResultExt<T> for Result<T, std::io::Error> {
    #[inline]
    fn to_sysexits(self) -> Result<T, crate::Exit> {
        self.map_err(|e| {
            let kind = e.kind();
            let code = io_to_sysexists(kind)
                .or_else(|| crate::bash::io_to_signal(kind))
                .unwrap_or(IO_ERR);
            crate::Exit::new(code).with_message(e)
        })
    }
}

/// Convert [`std::io::ErrorKind`] to a [`Code`][crate::Code]
#[inline]
pub fn io_to_sysexists(kind: std::io::ErrorKind) -> Option<crate::Code> {
    use std::io::ErrorKind::*;
    match kind {
        NotFound => Some(OS_FILE_ERR),
        PermissionDenied => Some(NO_PERM),
        ConnectionRefused | ConnectionReset | ConnectionAborted | NotConnected => {
            Some(PROTOCOL_ERR)
        }
        AddrInUse | AddrNotAvailable => Some(SERVICE_UNAVAILABLE),
        AlreadyExists => Some(CANT_CREAT),
        InvalidInput | InvalidData | UnexpectedEof => Some(DATA_ERR),
        WriteZero => Some(NO_INPUT),
        _ => None,
    }
}

/// The process exited successfully.
pub const OK: crate::Code = crate::Code::new(0);

/// The command was used incorrectly, e.g. with the wrong number of
/// arguments, a bad flag, bad syntax in a parameter, or whatever.
pub const USAGE_ERR: crate::Code = crate::Code::new(64);

/// The input data was incorrect in some way.  This should only be used for
/// user’s data and not system files.
pub const DATA_ERR: crate::Code = crate::Code::new(65);

/// An input file (not a system file) did not exist or was not readable.
/// This could also include errors like “No message” to a mailer (if it
/// cared to catch it).
pub const NO_INPUT: crate::Code = crate::Code::new(66);

/// The user specified did not exist.  This might be used for mail addresses
/// or remote logins.
pub const NO_USER: crate::Code = crate::Code::new(67);

/// The host specified did not exist.  This is used in mail addresses or
/// network requests.
pub const NO_HOST: crate::Code = crate::Code::new(68);

/// A service is unavailable.  This can occur if a support program or file
/// does not exist.  This can also be used as a catch-all message when
/// something you wanted to do doesn’t work, but you don’t know why.
pub const SERVICE_UNAVAILABLE: crate::Code = crate::Code::new(69);

/// An internal software error has been detected.  This should be limited
/// to non-operating system related errors if possible.
pub const SOFTWARE_ERR: crate::Code = crate::Code::new(70);

/// An operating system error has been detected.  This is intended to be
/// used for such things as “cannot fork”, or “cannot create pipe”.  It
/// includes things like [getuid(2)] returning a user that does not exist
/// in the passwd file.
///
/// [getuid(2)]: https://man.openbsd.org/getuid.2
pub const OS_ERR: crate::Code = crate::Code::new(71);

/// Some system file (e.g. _/etc/passwd_, _/var/run/utmp_) does not exist,
/// cannot be opened, or has some sort of error (e.g. syntax error).
pub const OS_FILE_ERR: crate::Code = crate::Code::new(72);

/// A (user specified) output file cannot be created.
pub const CANT_CREAT: crate::Code = crate::Code::new(73);

/// An error occurred while doing I/O on some file.
pub const IO_ERR: crate::Code = crate::Code::new(74);

/// Temporary failure, indicating something that is not really an error.
/// For example that a mailer could not create a connection, and the
/// request should be reattempted later.
pub const TEMP_FAIL: crate::Code = crate::Code::new(75);

/// The remote system returned something that was “not possible” during a
/// protocol exchange.
pub const PROTOCOL_ERR: crate::Code = crate::Code::new(76);

/// You did not have sufficient permission to perform the operation.  This
/// is not intended for file system problems, which should use `NoInput` or
/// `CantCreat`, but rather for high level permissions.
pub const NO_PERM: crate::Code = crate::Code::new(77);

/// Something was found in an unconfigured or misconfigured state.
pub const CONFIG_ERR: crate::Code = crate::Code::new(78);