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}