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}