Skip to main content

yui/
cli.rs

1use anyhow::Result;
2use camino::Utf8PathBuf;
3use clap::{Parser, Subcommand};
4
5use crate::cmd;
6
7#[derive(Parser, Debug)]
8#[command(version, about, long_about = None)]
9pub struct Cli {
10    /// Path to dotfiles source repository ($DOTFILES)
11    #[arg(short, long, env = "YUI_SOURCE", global = true)]
12    pub source: Option<Utf8PathBuf>,
13
14    /// Increase log verbosity (-v, -vv, -vvv)
15    #[arg(short, long, action = clap::ArgAction::Count, global = true)]
16    pub verbose: u8,
17
18    #[command(subcommand)]
19    pub command: Command,
20}
21
22#[derive(Subcommand, Debug)]
23pub enum Command {
24    /// Initialize source repo skeleton
25    Init {
26        /// Install git pre-commit/pre-push hooks for render-drift check
27        #[arg(long)]
28        git_hooks: bool,
29    },
30
31    /// Render templates + link targets + auto-absorb (default workflow)
32    Apply {
33        #[arg(long)]
34        dry_run: bool,
35    },
36
37    /// Render templates only
38    Render {
39        /// Fail with non-zero exit if rendered output diverges (CI hook)
40        #[arg(long)]
41        check: bool,
42        #[arg(long)]
43        dry_run: bool,
44    },
45
46    /// Link / relink targets only
47    Link {
48        #[arg(long)]
49        dry_run: bool,
50    },
51
52    /// Unlink targets
53    Unlink { paths: Vec<Utf8PathBuf> },
54
55    /// Show drift status (link-broken / replaced / template-drift)
56    Status,
57
58    /// Manually absorb a target into source (when auto-absorb skipped)
59    Absorb {
60        target: Utf8PathBuf,
61        #[arg(long)]
62        dry_run: bool,
63    },
64
65    /// Diagnose environment (symlink capability, source detection, etc)
66    Doctor,
67
68    /// Garbage-collect old backups
69    GcBackup {
70        /// e.g. "30d", "6m"
71        #[arg(long)]
72        older_than: Option<String>,
73    },
74}
75
76impl Cli {
77    pub fn run(self) -> Result<()> {
78        let source = self.source;
79        match self.command {
80            Command::Init { git_hooks } => cmd::init(source, git_hooks),
81            Command::Apply { dry_run } => cmd::apply(source, dry_run),
82            Command::Render { check, dry_run } => cmd::render(source, check, dry_run),
83            Command::Link { dry_run } => cmd::link(source, dry_run),
84            Command::Unlink { paths } => cmd::unlink(source, paths),
85            Command::Status => cmd::status(source),
86            Command::Absorb { target, dry_run } => cmd::absorb(source, target, dry_run),
87            Command::Doctor => cmd::doctor(source),
88            Command::GcBackup { older_than } => cmd::gc_backup(source, older_than),
89        }
90    }
91}