1use anyhow::Result;
2use camino::Utf8PathBuf;
3use clap::builder::styling::{AnsiColor, Effects, Styles};
4use clap::{Parser, Subcommand};
5
6use crate::cmd;
7use crate::config::IconsMode;
8
9const HELP_STYLES: Styles = Styles::styled()
16 .header(AnsiColor::BrightCyan.on_default().effects(Effects::BOLD))
18 .usage(AnsiColor::BrightCyan.on_default().effects(Effects::BOLD))
21 .literal(AnsiColor::Magenta.on_default().effects(Effects::BOLD))
24 .placeholder(AnsiColor::Cyan.on_default())
26 .error(AnsiColor::Red.on_default().effects(Effects::BOLD))
27 .valid(AnsiColor::Green.on_default())
28 .invalid(AnsiColor::Yellow.on_default().effects(Effects::BOLD));
29
30#[derive(Parser, Debug)]
31#[command(version, about, long_about = None, styles = HELP_STYLES)]
32pub struct Cli {
33 #[arg(short, long, env = "YUI_SOURCE", global = true)]
35 pub source: Option<Utf8PathBuf>,
36
37 #[arg(short, long, action = clap::ArgAction::Count, global = true)]
39 pub verbose: u8,
40
41 #[command(subcommand)]
42 pub command: Command,
43}
44
45#[derive(Subcommand, Debug)]
46pub enum Command {
47 Init {
49 #[arg(long)]
51 git_hooks: bool,
52 },
53
54 Apply {
56 #[arg(long)]
57 dry_run: bool,
58 },
59
60 Render {
62 #[arg(long)]
64 check: bool,
65 #[arg(long)]
66 dry_run: bool,
67 },
68
69 Link {
71 #[arg(long)]
72 dry_run: bool,
73 },
74
75 Unlink { paths: Vec<Utf8PathBuf> },
77
78 Status {
80 #[arg(long, value_name = "MODE")]
82 icons: Option<IconsMode>,
83 #[arg(long)]
85 no_color: bool,
86 },
87
88 List {
90 #[arg(long)]
92 all: bool,
93 #[arg(long, value_name = "MODE")]
95 icons: Option<IconsMode>,
96 #[arg(long)]
98 no_color: bool,
99 },
100
101 Absorb {
103 target: Utf8PathBuf,
104 #[arg(long)]
105 dry_run: bool,
106 },
107
108 Doctor,
110
111 GcBackup {
113 #[arg(long)]
115 older_than: Option<String>,
116 },
117
118 Hooks {
120 #[command(subcommand)]
121 action: HookAction,
122 },
123}
124
125#[derive(Subcommand, Debug)]
126pub enum HookAction {
127 List {
129 #[arg(long, value_name = "MODE")]
131 icons: Option<IconsMode>,
132 #[arg(long)]
134 no_color: bool,
135 },
136 Run {
141 name: Option<String>,
143 #[arg(long)]
145 force: bool,
146 },
147}
148
149impl Cli {
150 pub fn run(self) -> Result<()> {
151 let source = self.source;
152 match self.command {
153 Command::Init { git_hooks } => cmd::init(source, git_hooks),
154 Command::Apply { dry_run } => cmd::apply(source, dry_run),
155 Command::Render { check, dry_run } => cmd::render(source, check, dry_run),
156 Command::Link { dry_run } => cmd::link(source, dry_run),
157 Command::Unlink { paths } => cmd::unlink(source, paths),
158 Command::Status { icons, no_color } => cmd::status(source, icons, no_color),
159 Command::List {
160 all,
161 icons,
162 no_color,
163 } => cmd::list(source, all, icons, no_color),
164 Command::Absorb { target, dry_run } => cmd::absorb(source, target, dry_run),
165 Command::Doctor => cmd::doctor(source),
166 Command::GcBackup { older_than } => cmd::gc_backup(source, older_than),
167 Command::Hooks { action } => match action {
168 HookAction::List { icons, no_color } => cmd::hooks_list(source, icons, no_color),
169 HookAction::Run { name, force } => cmd::hooks_run(source, name, force),
170 },
171 }
172 }
173}