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<Urgency> 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(())
}