die_exit/
lib.rs

1// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6
7#[cfg(not(feature = "test"))]
8use std::process;
9
10/// const of 1
11pub const DEFAULT_EXIT_CODE: i32 = 1;
12
13#[cfg(feature = "red")]
14macro_rules! eprintln {
15    ($($arg:tt)*) => {
16        use std::io::Write;
17        writeln!(&mut ::std::io::stderr(), "\u{001b}[91;49;1m{}\u{001b}[0m", format!($($arg)*)).expect("failed printing to stderr");
18    };
19}
20
21/// Prints a message to [`stderr`] and terminates the current process with the specified exit code
22/// or 1 if no exit code is specified, by calling [`eprintln`]!() on all arguments followed by
23/// [process::exit(exit_code)][exit]
24///
25/// [`eprintln`]: https://doc.rust-lang.org/std/macro.eprintln.html
26/// [`stderr`]: https://doc.rust-lang.org/std/io/fn.stderr.html
27/// [exit]: https://doc.rust-lang.org/std/process/fn.exit.html
28///
29/// # Examples
30///
31/// Basic usage:
32///
33/// ```{.should_panic}
34/// # use die_exit::*;
35/// die!("argument to -e must be numeric"); // prints message to stderr then exits with code 1
36/// ```
37/// With custom error code:
38/// ```{.should_panic}
39/// # use die_exit::*;
40/// die!(2; "argument to -e must be numeric"); // prints message to stderr then exits with code 2
41/// ```
42/// error code can go at the beginning or end, just separate with colon:
43/// ```{.should_panic}
44/// # use die_exit::*;
45/// die!("argument to -e must be numeric"; 3); // prints message to stderr then exits with code 3
46/// ```
47/// supports all the formatting eprintln! does:
48/// ```{.should_panic}
49/// # use die_exit::*;
50/// die!("argument {} must be {}", "-e", 1; 4); // prints `argument -e must be 1` to stderr then exits with code 4
51/// ```
52/// supports all the formatting eprintln! does without exit code too:
53/// ```{.should_panic}
54/// # use die_exit::*;
55/// die!("argument {} must be {}", "-e", 1); // prints `argument -e must be 1` to stderr then exits with code 1
56/// ```
57/// just exit with a code alone:
58/// ```{.should_panic}
59/// # use die_exit::*;
60/// die!(2); // prints nothing, only exits with code 3
61/// ```
62/// just exit:
63/// ```{.should_panic}
64/// # use die_exit::*;
65/// die!(); // prints nothing, only exits with code 1
66/// ```
67#[cfg(not(feature = "test"))]
68#[macro_export]
69macro_rules! die {
70    () => (::std::process::exit(::die_exit::DEFAULT_EXIT_CODE));
71    ($x:expr) => (::die_exit::PrintExit::print_exit(&$x));
72    ($x:expr; $y:expr) => (::die_exit::PrintExit::print_exit(&($x, $y)));
73    ($x:expr; $($y:expr),+) => ({
74        eprintln!($($y),+);
75        ::std::process::exit($x)
76    });
77    ($($y:expr),+; $x:expr) => ({
78        eprintln!($($y),+);
79        ::std::process::exit($x)
80    });
81    ($($arg:tt)*) => ({
82        eprintln!($($arg)*);
83        ::std::process::exit(::die_exit::DEFAULT_EXIT_CODE)
84    });
85}
86#[cfg(feature = "test")]
87#[macro_export]
88macro_rules! die {
89    () => (
90        panic!("Exited with code {}", ::die_exit::DEFAULT_EXIT_CODE)
91    );
92    ($x:expr) => (::die_exit::PrintExit::print_exit(&$x));
93    ($x:expr; $y:expr) => (::die_exit::PrintExit::print_exit(&($x, $y)));
94    ($x:expr; $($y:expr),+) => ({
95        eprintln!($($y),+);
96        panic!("Exited with code {}", $x)
97    });
98    ($($y:expr),+; $x:expr) => ({
99        eprintln!($($y),+);
100        panic!("Exited with code {}", $x)
101    });
102    ($($arg:tt)*) => ({
103        eprintln!($($arg)*);
104        panic!("Exited with code {}", ::die_exit::DEFAULT_EXIT_CODE)
105    });
106}
107/// `Die` is a trait implemented on [`Result`] and [`Option`] to make exiting with messages and codes easy
108///
109/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
110/// [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
111pub trait Die<T> {
112    /// Unwraps a [`Result`] or [`Option`], yielding the content of an [`Ok`] or [`Some`].
113    ///
114    /// # Exits
115    ///
116    /// Calls [process::exit(1)][exit] if the value is an [`Err`] or [`None`], after printing the
117    /// passed message to [`stderr`].
118    ///
119    /// [`stderr`]: https://doc.rust-lang.org/std/io/fn.stderr.html
120    /// [exit]: https://doc.rust-lang.org/std/process/fn.exit.html
121    /// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
122    /// [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
123    /// [`Ok`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok
124    /// [`Err`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err
125    /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
126    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
127    ///
128    /// # Examples
129    ///
130    /// Basic usage:
131    ///
132    /// ```{.should_panic}
133    /// # use die_exit::Die;
134    /// let x: Result<u32, &str> = Err("emergency failure");
135    /// x.die("strange error"); // prints `strange error` to stderr then exits with code 1
136    /// ```
137    /// ```{.should_panic}
138    /// # use die_exit::Die;
139    /// let x: Option<u32> = None;
140    /// x.die("strange error"); // prints `strange error` to stderr then exits with code 1
141    /// ```
142    fn die(self, msg: &str) -> T;
143
144    /// Unwraps a [`Result`] or [`Option`], yielding the content of an [`Ok`] or [`Some`].
145    ///
146    /// # Exits
147    ///
148    /// Calls [process::exit(exit_code)][exit] if the value is an [`Err`] or [`None`], after printing the
149    /// passed message to [`stderr`].
150    ///
151    /// [`stderr`]: https://doc.rust-lang.org/std/io/fn.stderr.html
152    /// [exit]: https://doc.rust-lang.org/std/process/fn.exit.html
153    /// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
154    /// [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
155    /// [`Ok`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok
156    /// [`Err`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err
157    /// [`Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
158    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
159    ///
160    /// # Examples
161    ///
162    /// Basic usage:
163    ///
164    /// ```{.should_panic}
165    /// # use die_exit::Die;
166    /// let x: Result<u32, &str> = Err("emergency failure");
167    /// x.die_code("strange", 3); // prints `strange` to stderr then exits with code 3
168    /// ```
169    /// ```{.should_panic}
170    /// # use die_exit::Die;
171    /// let x: Option<u32> = None;
172    /// x.die_code("strange", 3); // prints `strange` to stderr then exits with code 3
173    /// ```
174    fn die_code(self, msg: &str, exit_code: i32) -> T;
175}
176
177/// `DieWith` is a trait implemented on [`Result`] only to make exiting with messages and codes easy
178///
179/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
180pub trait DieWith<T, E> {
181    /// Unwraps a [`Result`], yielding the content of an [`Ok`].
182    ///
183    /// # Exits
184    ///
185    /// Calls [process::exit(exit_code)][exit] if the value is an [`Err`], after printing the
186    /// message produced by the given function to [`stderr`].
187    ///
188    /// [`stderr`]: https://doc.rust-lang.org/std/io/fn.stderr.html
189    /// [exit]: https://doc.rust-lang.org/std/process/fn.exit.html
190    /// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
191    /// [`Ok`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok
192    /// [`Err`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err
193    ///
194    /// # Examples
195    ///
196    /// Basic usage:
197    ///
198    /// ```{.should_panic}
199    /// # use die_exit::DieWith;
200    /// let x: Result<u32, &str> = Err("emergency failure");
201    /// x.die_with(|err| format!("strange: {}", err)); // prints `strange: emergency failure` to stderr then exits with code 1
202    /// ```
203    /// ```{.should_panic}
204    /// # use die_exit::DieWith;
205    /// let x: Result<u32, &str> = Err("emergency failure");
206    /// x.die_with(|err| (format!("strange: {}", err), 3)); // prints `strange: emergency failure` to stderr then exits with code 3
207    /// ```
208    fn die_with<X: PrintExit, F: FnOnce(E) -> X>(self, func: F) -> T;
209}
210
211impl<T, E> Die<T> for Result<T, E> {
212    #[inline]
213    fn die(self, msg: &str) -> T {
214        self.die_code(msg, DEFAULT_EXIT_CODE)
215    }
216    #[inline]
217    fn die_code(self, msg: &str, exit_code: i32) -> T {
218        match self {
219            Ok(t) => t,
220            Err(_) => PrintExit::print_exit(&(exit_code, msg)),
221        }
222    }
223}
224
225impl<T> Die<T> for Option<T> {
226    #[inline]
227    fn die(self, msg: &str) -> T {
228        self.die_code(msg, DEFAULT_EXIT_CODE)
229    }
230    #[inline]
231    fn die_code(self, msg: &str, exit_code: i32) -> T {
232        match self {
233            Some(t) => t,
234            None => PrintExit::print_exit(&(exit_code, msg)),
235        }
236    }
237}
238
239impl<T, E> DieWith<T, E> for Result<T, E> {
240    #[inline]
241    fn die_with<X: PrintExit, F: FnOnce(E) -> X>(self, func: F) -> T {
242        match self {
243            Ok(t) => t,
244            Err(err) => PrintExit::print_exit(&func(err)),
245        }
246    }
247}
248
249pub trait PrintExit {
250    fn print_exit(&self) -> !;
251}
252
253impl PrintExit for i32 {
254    #[inline]
255    fn print_exit(&self) -> ! {
256        #[cfg(feature = "test")]
257        panic!("Exited with code {}", *self);
258        #[cfg(not(feature = "test"))]
259        process::exit(*self)
260    }
261}
262
263impl PrintExit for &str {
264    #[inline]
265    fn print_exit(&self) -> ! {
266        eprintln!("{}", self);
267        #[cfg(feature = "test")]
268        panic!("Exited with code {}", DEFAULT_EXIT_CODE);
269        #[cfg(not(feature = "test"))]
270        process::exit(DEFAULT_EXIT_CODE)
271    }
272}
273
274impl PrintExit for String {
275    #[inline]
276    fn print_exit(&self) -> ! {
277        eprintln!("{}", self);
278        #[cfg(feature = "test")]
279        panic!("Exited with code {}", DEFAULT_EXIT_CODE);
280        #[cfg(not(feature = "test"))]
281        process::exit(DEFAULT_EXIT_CODE)
282    }
283}
284
285impl PrintExit for (i32, &str) {
286    #[inline]
287    fn print_exit(&self) -> ! {
288        eprintln!("{}", self.1);
289        #[cfg(feature = "test")]
290        panic!("Exited with code {}", self.0);
291        #[cfg(not(feature = "test"))]
292        process::exit(self.0)
293    }
294}
295
296impl PrintExit for (i32, String) {
297    #[inline]
298    fn print_exit(&self) -> ! {
299        eprintln!("{}", self.1);
300        #[cfg(feature = "test")]
301        panic!("Exited with code {}", self.0);
302        #[cfg(not(feature = "test"))]
303        process::exit(self.0)
304    }
305}
306
307impl PrintExit for (&str, i32) {
308    #[inline]
309    fn print_exit(&self) -> ! {
310        eprintln!("{}", self.0);
311        #[cfg(feature = "test")]
312        panic!("Exited with code {}", self.1);
313        #[cfg(not(feature = "test"))]
314        process::exit(self.1)
315    }
316}
317
318impl PrintExit for (String, i32) {
319    #[inline]
320    fn print_exit(&self) -> ! {
321        eprintln!("{}", self.0);
322        #[cfg(feature = "test")]
323        panic!("Exited with code {}", self.1);
324        #[cfg(not(feature = "test"))]
325        process::exit(self.1)
326    }
327}