211 lines
5.3 KiB
Rust
211 lines
5.3 KiB
Rust
mod commands;
|
|
|
|
use clap::{Parser, Subcommand, ValueEnum};
|
|
use std::path::PathBuf;
|
|
use tracing_subscriber::EnvFilter;
|
|
|
|
#[derive(Parser)]
|
|
#[command(name = "doot", version)]
|
|
#[command(about = "A modern dotfiles manager with a typed DSL", long_about = None)]
|
|
struct Cli {
|
|
#[command(subcommand)]
|
|
command: Commands,
|
|
|
|
/// Increase verbosity (-v = info, -vv = debug, -vvv = trace)
|
|
#[arg(short, long, global = true, action = clap::ArgAction::Count)]
|
|
verbose: u8,
|
|
|
|
/// Suppress all log output
|
|
#[arg(short, long, global = true)]
|
|
quiet: bool,
|
|
|
|
#[arg(short = 'C', long, global = true)]
|
|
config: Option<PathBuf>,
|
|
|
|
/// Log level (overrides -v/-q flags)
|
|
#[arg(long, global = true)]
|
|
log_level: Option<LogLevelArg>,
|
|
|
|
/// Log output format
|
|
#[arg(long, global = true, default_value = "text")]
|
|
log_format: LogFormatArg,
|
|
}
|
|
|
|
#[derive(Clone, ValueEnum)]
|
|
enum LogLevelArg {
|
|
Trace,
|
|
Debug,
|
|
Info,
|
|
Warn,
|
|
Error,
|
|
}
|
|
|
|
#[derive(Clone, ValueEnum)]
|
|
enum LogFormatArg {
|
|
Text,
|
|
Json,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum Commands {
|
|
Init {
|
|
/// Source directory for dotfiles (default: ~/.config/doot)
|
|
path: Option<PathBuf>,
|
|
},
|
|
|
|
Apply {
|
|
#[arg(short = 'n', long)]
|
|
dry_run: bool,
|
|
|
|
/// Auto-uninstall packages removed from config
|
|
#[arg(long)]
|
|
prune: bool,
|
|
},
|
|
|
|
Diff {
|
|
#[arg(short, long)]
|
|
all: bool,
|
|
},
|
|
|
|
Status,
|
|
|
|
Check,
|
|
|
|
Fmt {
|
|
#[arg(short, long)]
|
|
check: bool,
|
|
},
|
|
|
|
Rollback {
|
|
snapshot: Option<String>,
|
|
},
|
|
|
|
Snapshot {
|
|
name: String,
|
|
},
|
|
|
|
Encrypt {
|
|
file: PathBuf,
|
|
|
|
#[arg(short, long)]
|
|
recipient: Option<String>,
|
|
},
|
|
|
|
Decrypt {
|
|
file: PathBuf,
|
|
|
|
#[arg(short, long)]
|
|
identity: Option<PathBuf>,
|
|
},
|
|
|
|
Package {
|
|
#[command(subcommand)]
|
|
action: PackageAction,
|
|
},
|
|
|
|
Lsp,
|
|
|
|
Tui,
|
|
|
|
/// Open source file in editor for a deployed target
|
|
Edit {
|
|
/// Target path or dotfile name (e.g., ~/.config/nvim or nvim)
|
|
target: String,
|
|
|
|
/// Apply changes after editing
|
|
#[arg(short, long)]
|
|
apply: bool,
|
|
|
|
/// Skip confirmation prompt
|
|
#[arg(short = 'y', long)]
|
|
yes: bool,
|
|
},
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum PackageAction {
|
|
Install,
|
|
Update,
|
|
List,
|
|
}
|
|
|
|
fn main() -> anyhow::Result<()> {
|
|
let cli = Cli::parse();
|
|
|
|
// Priority: DOOT_LOG env > --log-level flag > -q/v flags > default (warn)
|
|
let default_level = if let Some(ref level) = cli.log_level {
|
|
match level {
|
|
LogLevelArg::Trace => "trace",
|
|
LogLevelArg::Debug => "debug",
|
|
LogLevelArg::Info => "info",
|
|
LogLevelArg::Warn => "warn",
|
|
LogLevelArg::Error => "error",
|
|
}
|
|
} else if cli.quiet {
|
|
"off"
|
|
} else {
|
|
match cli.verbose {
|
|
0 => "warn",
|
|
1 => "info",
|
|
2 => "debug",
|
|
_ => "trace",
|
|
}
|
|
};
|
|
|
|
// Scope doot crates to chosen level, third-party at warn
|
|
// -vvvv+ enables trace for ALL crates including deps
|
|
let default_directive = if default_level == "off" {
|
|
"off".to_string()
|
|
} else if cli.verbose >= 4 {
|
|
default_level.to_string()
|
|
} else {
|
|
format!(
|
|
"doot_cli={l},doot_core={l},doot_lang={l},warn",
|
|
l = default_level
|
|
)
|
|
};
|
|
|
|
let env_filter =
|
|
EnvFilter::try_from_env("DOOT_LOG").unwrap_or_else(|_| EnvFilter::new(&default_directive));
|
|
|
|
match cli.log_format {
|
|
LogFormatArg::Json => {
|
|
tracing_subscriber::fmt()
|
|
.json()
|
|
.with_env_filter(env_filter)
|
|
.with_target(true)
|
|
.with_writer(std::io::stderr)
|
|
.init();
|
|
}
|
|
LogFormatArg::Text => {
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter(env_filter)
|
|
.with_target(true)
|
|
.with_writer(std::io::stderr)
|
|
.init();
|
|
}
|
|
}
|
|
|
|
match cli.command {
|
|
Commands::Init { path } => commands::init::run(path),
|
|
Commands::Apply { dry_run, prune } => commands::apply::run(cli.config, dry_run, prune),
|
|
Commands::Diff { all } => commands::diff::run(cli.config, all),
|
|
Commands::Status => commands::status::run(cli.config),
|
|
Commands::Check => commands::check::run(cli.config),
|
|
Commands::Fmt { check } => commands::fmt::run(cli.config, check),
|
|
Commands::Rollback { snapshot } => commands::rollback::run(cli.config, snapshot),
|
|
Commands::Snapshot { name } => commands::snapshot::run(cli.config, name),
|
|
Commands::Encrypt { file, recipient } => commands::encrypt::run(file, recipient),
|
|
Commands::Decrypt { file, identity } => commands::decrypt::run(file, identity),
|
|
Commands::Package { action } => match action {
|
|
PackageAction::Install => commands::package::install(cli.config),
|
|
PackageAction::Update => commands::package::update(),
|
|
PackageAction::List => commands::package::list(cli.config),
|
|
},
|
|
Commands::Lsp => commands::lsp::run(),
|
|
Commands::Tui => commands::tui::run(cli.config),
|
|
Commands::Edit { target, apply, yes } => {
|
|
commands::edit::run(cli.config, target, apply, yes)
|
|
}
|
|
}
|
|
}
|