use anyhow::{Context, Result}; use clap::Parser; use config::LoggingPreset; use indicatif::MultiProgress; use pile_toolbox::cancelabletask::CancelableTaskResult; use std::process::ExitCode; use tracing::{error, warn}; use tracing_indicatif::{IndicatifWriter, writer::Stderr}; use tracing_subscriber::fmt::MakeWriter; use crate::{ command::{CliCmd, CliCmdDispatch, SubCommand}, signal::start_signal_task, }; mod cli; mod command; mod config; mod signal; #[derive(Parser, Debug)] #[command(version, about, long_about = None, styles=cli::clap_styles())] struct Cli { /// Increase verbosity (can be repeated) #[arg(short, action = clap::ArgAction::Count,global = true)] v: u8, /// Decrease verbosity (can be repeated) #[arg(short, action = clap::ArgAction::Count, global = true)] q: u8, #[command(subcommand)] cmd: SubCommand, } #[derive(Clone)] pub struct GlobalContext { pub mp: MultiProgress, } fn main() -> ExitCode { #[expect(clippy::unwrap_used)] let rt = tokio::runtime::Builder::new_multi_thread() .enable_all() .worker_threads(10) .build() .unwrap(); match rt.block_on(main_inner()) { Ok(code) => { std::process::exit(code); } Err(err) => { for e in err.chain() { error!("{}", e); } std::process::exit(1); } } } async fn main_inner() -> Result { let cli = Cli::parse(); let level_i: i16 = cli.v as i16 - cli.q as i16; let level; if level_i <= -2 { level = LoggingPreset::Error } else if level_i == -1 { level = LoggingPreset::Warn } else if level_i == 0 { level = LoggingPreset::Info } else if level_i == 1 { level = LoggingPreset::Debug } else if level_i >= 2 { level = LoggingPreset::Trace } else { unreachable!() } let mp = MultiProgress::new(); let writer: IndicatifWriter = IndicatifWriter::new(mp.clone()); tracing_subscriber::fmt() .with_env_filter(level.get_config()) .without_time() .with_ansi(true) .with_writer(writer.make_writer()) .init(); let ctx = GlobalContext { mp }; let task = cli.cmd.start(ctx).context("while starting task")?; let signal_task = start_signal_task(task.flag().clone()); match task.join().await { Ok(CancelableTaskResult::Finished(Ok(code))) => Ok(code), Ok(CancelableTaskResult::Cancelled) => { signal_task.abort(); warn!("Task cancelled successfully"); Ok(1) } Err(err) => { signal_task.abort(); Err(err).context("while joining task") } Ok(CancelableTaskResult::Finished(Err(err))) => { signal_task.abort(); Err(err).context("while running task") } } }