1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
//! This crate allows cleanly exiting a program using a custom exit code,
//! without the drawbacks of [`process::exit`]. Destructors will be called as
//! usual, and the stack will be unwound to the main function.
//!
//! It is always required to attach [`#[main]`][attribute] to the main
//! function. Then, [`with_code`] can be called from almost anywhere in the
//! program. Restrictions are noted in the documentation for that function.
//!
//! # Implementation
//!
//! Internally, this crate uses panicking to unwind the stack. Thus, if
//! panicking is set to "abort" instead of the default "unwind", setting the
//! exit status will not work correctly. Changing this option may become an
//! error in the future, if it can be detected at compile time.
//!
//! Additionally, the program will not exit if [`with_code`] is called from a
//! spawned thread, unless panics are propagated from that thread.
//!
//! # Examples
//!
//! ```
//! use std::env;
//!
//! fn read_args() {
//!     if env::args_os().nth(1).is_some() {
//!         eprintln!("too many arguments");
//!         quit::with_code(1);
//!     }
//! }
//!
//! #[quit::main]
//! fn main() {
//!     read_args();
//! }
//! ```
//!
//! [attribute]: main

#![forbid(unsafe_code)]
#![warn(unused_results)]

use std::panic;
use std::panic::UnwindSafe;
use std::process;

// https://github.com/rust-lang/rust/issues/62127
/// Modifies the main function to exit with the code passed to [`with_code`].
///
/// This attribute should always be attached to the main function. Otherwise,
/// the exit code of the program may be incorrect.
///
/// # Examples
///
/// ```
/// #[quit::main]
/// fn main() {}
/// ```
#[cfg(not(test))]
pub use quit_macros::main;

#[derive(Copy, Clone)]
#[must_use]
struct ExitCode(i32);

#[doc(hidden)]
#[inline]
pub fn __run<TMainFn, TReturn>(main_fn: TMainFn) -> TReturn
where
    TMainFn: FnOnce() -> TReturn + UnwindSafe,
{
    panic::catch_unwind(main_fn).unwrap_or_else(|payload| {
        if let Some(&ExitCode(exit_code)) = payload.downcast_ref() {
            process::exit(exit_code);
        }
        panic::resume_unwind(payload);
    })
}

/// Cleanly exits the program with an exit code.
///
/// Calling this function from within an FFI boundary invokes undefined
/// behavior. Because panics are used internally to unwind the stack, the exit
/// code cannot be passed safely. [`process::exit`] should be used instead in
/// that case.
///
/// This function will not behave as expected unless [`#[main]`][attribute] is
/// attached to the main function. Other implementation notes are mentioned in
/// [the module-level documentation][implementation].
///
/// # Examples
///
/// ```should_panic
/// fn exit() -> ! {
///     quit::with_code(1);
/// }
/// # exit();
/// ```
///
/// [attribute]: main
/// [implementation]: self#implementation
#[inline]
pub fn with_code(exit_code: i32) -> ! {
    panic::resume_unwind(Box::new(ExitCode(exit_code)));
}