use std::{
process::{ExitCode, Termination},
time::Duration,
};
use binstalk::{errors::BinstallError, helpers::tasks::AutoAbortJoinHandle};
use miette::Result;
use tokio::runtime::Runtime;
use tracing::{error, info, warn};
use crate::signal::cancel_on_user_sig_term;
pub enum MainExit {
Success(Option<Duration>),
Error(BinstallError),
Report(miette::Report),
}
impl Termination for MainExit {
fn report(self) -> ExitCode {
let (code, err) = match self {
Self::Success(spent) => {
if let Some(spent) = spent {
info!("Done in {spent:?}");
}
return ExitCode::SUCCESS;
}
Self::Error(err) => {
let code = err.exit_code();
let Some(report) = err.get_report() else {
warn!("Installation cancelled");
return code;
};
(code, report)
}
Self::Report(err) => (ExitCode::from(16), err),
};
error!("Fatal error:");
println!("{err:?}");
code
}
}
impl MainExit {
pub fn new(res: Result<()>, done: Option<Duration>) -> Self {
res.map(|()| MainExit::Success(done)).unwrap_or_else(|err| {
err.downcast::<BinstallError>()
.map(MainExit::Error)
.unwrap_or_else(MainExit::Report)
})
}
}
pub fn run_tokio_main(
f: impl FnOnce() -> Result<Option<AutoAbortJoinHandle<Result<()>>>>,
) -> Result<()> {
let rt = Runtime::new().map_err(BinstallError::from)?;
let _guard = rt.enter();
if let Some(handle) = f()? {
rt.block_on(cancel_on_user_sig_term(handle))?
} else {
Ok(())
}
}