use std::{
fmt::{Debug, Display},
marker::PhantomData,
panic::{self, PanicInfo},
};
pub struct Terminate<E>
where
E: Display + Debug,
{
at_exit: Option<fn()>,
on_error: Option<fn(E) -> E>,
install: Option<fn() -> Result<(), E>>,
error: PhantomData<E>,
}
impl<E> Terminate<E>
where
E: Display + Debug,
{
pub fn new() -> Self {
Self {
on_error: None,
at_exit: None,
install: None,
error: PhantomData,
}
}
pub fn install(mut self, install: fn() -> Result<(), E>) -> Self {
self.install = Some(install);
self
}
pub fn replace_panic(self, panic: impl Fn(&PanicInfo<'_>) + Send + Sync + 'static) -> Self {
panic::set_hook(Box::new(panic));
self
}
pub fn panic_with(self, panic: fn(&PanicInfo<'_>)) -> Self {
let original_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
panic(&panic_info);
original_hook(&panic_info);
}));
self
}
pub fn on_error(mut self, on_error: fn(E) -> E) -> Self {
self.on_error = Some(on_error);
self
}
pub fn at_exit(mut self, at_exit: fn()) -> Self {
self.at_exit = Some(at_exit);
self
}
pub fn execute(self, main: fn() -> Result<(), E>) -> Result<(), E> {
if let Some(install) = self.install {
let mut res = install();
res = match (self.on_error, res) {
(Some(on_error), Err(err)) => Err(on_error(err)),
(_, res) => res,
};
if let Some(at_exit) = self.at_exit {
at_exit()
}
res?;
}
let mut res = main();
res = match (self.on_error, res) {
(Some(on_error), Err(err)) => Err(on_error(err)),
(_, res) => res,
};
if let Some(at_exit) = self.at_exit {
at_exit()
}
res
}
}