#![forbid(unsafe_code)]
#![warn(unused_results)]
#[cfg(not(panic = "unwind"))]
compile_error!(
r#"Quit requires unwinding panics:
https://docs.rs/quit/latest/quit/#implementation"#
);
use std::panic;
use std::panic::UnwindSafe;
use std::process;
use std::process::Termination;
use std::sync::atomic;
use std::sync::atomic::AtomicBool;
#[cfg(all(feature = "__unstable_tests", not(quit_docs_rs)))]
#[doc(hidden)]
pub mod tests_common;
pub use quit_macros::main;
struct ExitCode(process::ExitCode);
enum ResultInner<T>
where
T: Termination,
{
Result(T),
ExitCode(process::ExitCode),
}
#[doc(hidden)]
pub struct __Result<T>(ResultInner<T>)
where
T: Termination;
impl<T> Termination for __Result<T>
where
T: Termination,
{
#[inline]
fn report(self) -> process::ExitCode {
use ResultInner as Inner;
match self.0 {
Inner::Result(result) => result.report(),
Inner::ExitCode(exit_code) => exit_code,
}
}
}
#[doc(hidden)]
#[inline]
pub fn __run<F, R>(main_fn: F) -> __Result<R>
where
F: FnOnce() -> R + UnwindSafe,
R: Termination,
{
panic::catch_unwind(main_fn)
.map(|x| __Result(ResultInner::Result(x)))
.unwrap_or_else(|payload| {
if let Some(&ExitCode(exit_code)) = payload.downcast_ref() {
__Result(ResultInner::ExitCode(exit_code))
} else {
panic::resume_unwind(payload);
}
})
}
#[doc(hidden)]
pub static __ATTACHED: AtomicBool = AtomicBool::new(false);
#[doc(hidden)]
#[macro_export]
macro_rules! __main {
(
( $($signature_token:tt)+ )
( $($args_token:tt)* ) -> $return_type:ty { $($body_token:tt)* }
) => {
$($signature_token)+ (
$($args_token)*
) -> $crate::__Result<$return_type> {
$crate::__ATTACHED.store(
true,
::std::sync::atomic::Ordering::Release,
);
$crate::__run(|| { $($body_token)* })
}
};
( $signature:tt $args:tt $body:tt ) => {
$crate::__main!($signature $args -> () $body);
}
}
#[inline]
pub fn with_code<T>(exit_code: T) -> !
where
T: Into<process::ExitCode>,
{
assert!(
__ATTACHED.load(atomic::Ordering::Acquire),
"`#[quit::main]` has not been attached to `main`",
);
panic::resume_unwind(Box::new(ExitCode(exit_code.into())));
}