use anyhow::Result;
use camino::Utf8PathBuf;
use clap::{ArgAction, Parser, Subcommand};
use tracing::*;
use backpak::config;
use backpak::counters;
use backpak::ui::*;
#[derive(Debug, Parser)]
struct Args {
#[clap(short, long, action(ArgAction::Count))]
verbose: u8,
#[clap(short, long, value_enum, default_value = "auto")]
color: Color,
#[clap(long, verbatim_doc_comment)]
config: Option<Utf8PathBuf>,
#[clap(short, long, verbatim_doc_comment)]
timestamps: bool,
#[clap(short = 'C', long, name = "PATH")]
working_directory: Option<Utf8PathBuf>,
#[clap(short, long)]
repository: Utf8PathBuf,
#[clap(subcommand)]
subcommand: Command,
}
#[derive(Debug, Copy, Clone, clap::ValueEnum)]
enum Color {
Auto,
Always,
Never,
}
#[derive(Debug, Subcommand)]
enum Command {
Init(init::Args),
Backup(backup::Args),
Cat(cat::Args),
Check(check::Args),
Copy(copy::Args),
Diff(diff::Args),
Dump(dump::Args),
FilterSnapshot(filter_snapshot::Args),
Forget(forget::Args),
Ls(ls::Args),
Prune(prune::Args),
Restore(restore::Args),
Snapshots(snapshots::Args),
RebuildIndex(rebuild_index::Args),
Usage,
}
fn main() {
run().unwrap_or_else(|e| {
error!("{:?}", e);
std::process::exit(1);
});
}
fn run() -> Result<()> {
let args = Args::parse();
let logmode = match args.subcommand {
Command::Cat(_) | Command::Diff(_) | Command::Dump(_) | Command::Ls(_) => LogMode::Quiet,
_ => LogMode::InfoStdout,
};
init_logger(&args, logmode);
let conf = config::load(args.config)?;
if let Some(dir) = &args.working_directory {
std::env::set_current_dir(dir).expect("Couldn't change working directory");
}
match args.subcommand {
Command::Init(i) => init::run(&args.repository, i),
Command::Backup(b) => backup::run(conf, &args.repository, b),
Command::Cat(c) => cat::run(&conf, &args.repository, c),
Command::Check(c) => check::run(&conf, &args.repository, c),
Command::Copy(c) => copy::run(conf, &args.repository, c),
Command::Diff(d) => diff::run(&conf, &args.repository, d),
Command::Dump(d) => dump::run(&conf, &args.repository, d),
Command::FilterSnapshot(f) => filter_snapshot::run(conf, &args.repository, f),
Command::Forget(f) => forget::run(&conf, &args.repository, f),
Command::Ls(l) => ls::run(&conf, &args.repository, l),
Command::Prune(p) => prune::run(&conf, &args.repository, p),
Command::Restore(r) => restore::run(conf, &args.repository, r),
Command::Snapshots(s) => snapshots::run(&conf, &args.repository, s),
Command::RebuildIndex(r) => rebuild_index::run(&conf, &args.repository, r),
Command::Usage => usage::run(&conf, &args.repository),
}?;
counters::log_counts();
Ok(())
}
enum LogMode {
InfoStdout,
Quiet,
}
fn init_logger(args: &Args, m: LogMode) {
use tracing_subscriber::prelude::*;
let level = match args.verbose {
0 => Level::WARN,
1 => Level::INFO,
2 => Level::DEBUG,
_ => Level::TRACE,
};
let ansis = match args.color {
Color::Always => true,
Color::Auto => {
use std::io::IsTerminal;
std::io::stderr().is_terminal()
}
Color::Never => false,
};
let stderr_layer = tracing_subscriber::fmt::layer()
.with_writer(std::io::stderr.with_max_level(level))
.with_ansi(ansis);
let stderr_layer = if level == Level::TRACE {
stderr_layer.with_span_events(tracing_subscriber::fmt::format::FmtSpan::CLOSE)
} else {
stderr_layer.with_target(false)
};
let stderr_layer = if args.timestamps {
stderr_layer
.with_timer(tracing_subscriber::fmt::time::SystemTime)
.boxed()
} else {
stderr_layer.without_time().boxed()
};
let stdout_layer = tracing_subscriber::fmt::layer()
.with_writer(
std::io::stdout
.with_max_level(Level::INFO)
.with_min_level(Level::INFO),
)
.without_time()
.with_level(false)
.with_target(false)
.with_ansi(ansis);
let reg = tracing_subscriber::registry().with(stderr_layer);
match m {
LogMode::Quiet => reg.init(),
LogMode::InfoStdout => reg.with(stdout_layer).init(),
}
}