use std::process::ExitCode;
use clap::Parser;
use youtube_legend_cli::cli::{load_config, Cli};
use youtube_legend_cli::error::AppError;
use youtube_legend_cli::logging::init_tracing;
use youtube_legend_cli::run;
use mimalloc::MiMalloc;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
fn main() -> ExitCode {
let mut cli = Cli::parse();
if let Some(path) = cli.config.clone() {
match load_config(&path) {
Ok(overrides) => cli.apply_config_overrides(overrides),
Err(e) => {
eprintln!("config error: {e}");
return ExitCode::from(e.exit_code());
}
}
}
cli.apply_overrides();
if let Err(e) = init_tracing(cli.log_level, cli.log_format, cli.color, cli.quiet) {
eprintln!("tracing init failed: {e}");
return ExitCode::from(e.exit_code());
}
let shutdown_token = tokio_util::sync::CancellationToken::new();
let worker_threads = std::thread::available_parallelism()
.map(|n| n.get().clamp(2, 8))
.unwrap_or(4);
let runtime = match tokio::runtime::Builder::new_multi_thread()
.worker_threads(worker_threads)
.enable_all()
.build()
{
Ok(rt) => rt,
Err(e) => {
tracing::error!(error = %e, "failed to start tokio runtime");
return ExitCode::from(AppError::Internal(format!("tokio runtime: {e}")).exit_code());
}
};
let exit_code = runtime.block_on(async move {
let signal_watcher = tokio::spawn(install_signal_handler(shutdown_token.clone()));
let result = tokio::select! {
biased;
result = run(cli) => result,
_ = shutdown_token.cancelled() => {
tracing::warn!("cancellation requested before completion");
Ok(ExitCode::from(130))
}
};
drop(signal_watcher);
result
});
match exit_code {
Ok(code) => code,
Err(e) => ExitCode::from(e.exit_code()),
}
}
#[cfg(unix)]
async fn install_signal_handler(token: tokio_util::sync::CancellationToken) {
use tokio::signal::unix::{signal, SignalKind};
let mut sigterm = match signal(SignalKind::terminate()) {
Ok(s) => s,
Err(e) => {
tracing::warn!(error = %e, "could not install SIGTERM handler");
return;
}
};
let mut sigint = match signal(SignalKind::interrupt()) {
Ok(s) => s,
Err(e) => {
tracing::warn!(error = %e, "could not install SIGINT handler");
return;
}
};
let mut first = true;
loop {
tokio::select! {
biased;
_ = sigterm.recv() => {},
_ = sigint.recv() => {},
}
if first {
tracing::info!(target: "events", event = "signal", "shutdown requested");
token.cancel();
first = false;
} else {
tracing::warn!(target: "events", event = "signal", "second signal received; forcing immediate exit");
std::process::exit(130);
}
}
}
#[cfg(not(unix))]
async fn install_signal_handler(token: tokio_util::sync::CancellationToken) {
let mut first = true;
loop {
if let Err(e) = tokio::signal::ctrl_c().await {
tracing::warn!(error = %e, "could not install SIGINT handler");
return;
}
if first {
tracing::info!(target: "events", event = "signal", "shutdown requested");
token.cancel();
first = false;
} else {
tracing::warn!(target: "events", event = "signal", "second signal received; forcing immediate exit");
std::process::exit(130);
}
}
}