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