1use std::io::Write;
2
3pub type ExitResult = Result<(), Exit>;
5
6pub 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 std::fmt::Display::fmt(self, f)
50 }
51}
52
53pub trait WithCodeResultExt<T> {
55 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#[inline]
68pub fn exit(result: ExitResult) -> ! {
69 let code = report(result);
70 code.process_exit()
71}
72
73#[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 let _ = writeln!(std::io::stderr(), "{}", msg);
83 }
84 err.code
85 }
86 }
87}