use backup::Backup; use clap::Parser; use cli::Subcommands; use config::Config; use error::{Error, Result}; use tracing::{debug, error, level_filters::LevelFilter}; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; mod backup; mod cli; mod config; mod error; mod packages; mod pathinfo; fn main() -> Result<()> { let file_appender = tracing_appender::rolling::never("./", "arbs.log"); let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender); tracing_subscriber::registry() .with( fmt::layer() .with_writer(non_blocking) .with_file(false) .with_ansi(false) .without_time(), ) .with(fmt::layer().with_file(false).without_time()) .with( EnvFilter::builder() .with_default_directive(LevelFilter::INFO.into()) .from_env_lossy(), ) .init(); debug!("logging initialized"); match run_cli() { Ok(()) => { println!("OK") ; Ok(())}, Err(err) => { error!(?err); error!("{:?}", std::error::Error::source(&err)); send_notification("Backup Error", &err.to_string(), Urgency::Critical)?; Err(err) } } } fn run_cli() -> Result<()> { let cli = cli::Cli::parse(); let config = Config::load(cli.config)?; match cli.subcommand { Subcommands::GenerateConfig => Config::generate()?, Subcommands::Save => { let backup = Backup::create(&config)?; backup.save(&config)?; } Subcommands::Restore { package_install } => { let Some(last_backup) = Backup::get_last(&config)? else { return Err(Error::BackupNotFound)?; }; if package_install { last_backup.packages.install()?; } last_backup.restore(&config)?; }, }; Ok(()) } enum Urgency { Normal, Critical } #[cfg(feature = "notifications")] impl From for notify_rust::Urgency { fn from(value: Urgency) -> Self { match value { Urgency::Normal => Self::Normal, Urgency::Critical => Self::Critical, } } } fn send_notification(summary: &str, body: &str, urgency: Urgency) -> Result<()> { #[cfg(feature = "notifications")] { let Some(mut icon) = dirs::data_dir() else { return Err(Error::NoSysDir); }; icon.push(env!("CARGO_PKG_NAME")); icon.push("icon.png"); notify_rust::Notification::new() .summary(summary) .body(body) .icon(&icon.to_string_lossy()) .timeout(0) .urgency(urgency.into()) .show()?; } Ok(()) }