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