use std::{ future::Future, process::{ExitCode, Termination}, time::Duration, }; use binstalk::errors::BinstallError; use binstalk::helpers::{signal::cancel_on_user_sig_term, tasks::AutoAbortJoinHandle}; use log::{error, info}; use miette::Result; use tokio::runtime::Runtime; pub enum MainExit { Success(Duration), Error(BinstallError), Report(miette::Report), } impl Termination for MainExit { fn report(self) -> ExitCode { match self { Self::Success(spent) => { info!("Done in {spent:?}"); ExitCode::SUCCESS } Self::Error(err) => err.report(), Self::Report(err) => { error!("Fatal error:\n{err:?}"); ExitCode::from(16) } } } } impl MainExit { pub fn new(result: Result, BinstallError>, done: Duration) -> Self { result.map_or_else(MainExit::Error, |res| { res.map(|()| MainExit::Success(done)).unwrap_or_else(|err| { err.downcast::() .map(MainExit::Error) .unwrap_or_else(MainExit::Report) }) }) } } /// This function would start a tokio multithreading runtime, /// spawn a new task on it that runs `f`, then `block_on` it. /// /// It will cancel the future if user requested cancellation /// via signal. pub fn run_tokio_main(f: F) -> Result where F: Future + Send + 'static, T: Send + 'static, { let rt = Runtime::new()?; let handle = AutoAbortJoinHandle::new(rt.spawn(f)); rt.block_on(cancel_on_user_sig_term(handle)) }