garden/
cli.rs

1use clap::{Parser, Subcommand, ValueHint};
2
3use crate::{cmds, constants, model, path};
4
5#[derive(Clone, Debug, Default, Parser)]
6#[command(name = constants::GARDEN)]
7#[command(author, version, about, long_about = None)]
8pub struct MainOptions {
9    /// Use ANSI colors [auto, true, false, on, off, always, never, 1, 0]
10    #[arg(
11        long,
12        require_equals = true,
13        num_args = 0..=1,
14        default_value_t = model::ColorMode::Auto,
15        default_missing_value = "true",
16        value_name = "WHEN",
17        value_parser = model::ColorMode::parse_from_str,
18    )]
19    pub color: model::ColorMode,
20
21    /// Set the Garden file to use
22    #[arg(long, short, value_hint = ValueHint::FilePath)]
23    pub config: Option<std::path::PathBuf>,
24
25    /// Change directories before searching for Garden files
26    #[arg(long, short = 'C', value_hint = ValueHint::DirPath)]
27    pub chdir: Option<std::path::PathBuf>,
28
29    /// Increase verbosity for a debug category
30    #[arg(long, short, action = clap::ArgAction::Append)]
31    pub debug: Vec<String>,
32
33    /// Set variables using 'name=value' expressions
34    #[arg(long, short = 'D')]
35    pub define: Vec<String>,
36
37    /// Set the Garden tree root
38    #[arg(long, short, value_hint = ValueHint::DirPath)]
39    pub root: Option<std::path::PathBuf>,
40
41    /// Be quiet
42    #[arg(short, long)]
43    pub quiet: bool,
44
45    /// Increase verbosity level (default: 0)
46    #[arg(short, long, action = clap::ArgAction::Count)]
47    pub verbose: u8,
48
49    /// Command to run
50    #[command(subcommand)]
51    pub command: Command,
52}
53
54impl MainOptions {
55    /// Construct a default set of options
56    pub fn new() -> Self {
57        Self::default()
58    }
59}
60
61/// Parse a vector of debug level arguments to count how many have been set
62pub fn debug_level(debug: &[String], name: &str) -> u8 {
63    debug.iter().filter(|&x| x == name).count() as u8
64}
65
66/// Garden command-line options that are common to all commands and typically specified before
67/// sub-command options and arguments.
68pub trait GardenOptions {
69    fn get_chdir(&self) -> &Option<std::path::PathBuf>;
70    fn get_color_mut(&mut self) -> &mut model::ColorMode;
71    fn get_config(&self) -> &Option<std::path::PathBuf>;
72    fn set_config(&mut self, path: std::path::PathBuf);
73    fn get_debug(&self) -> &[String];
74    fn get_root(&self) -> &Option<std::path::PathBuf>;
75    fn set_root(&mut self, path: std::path::PathBuf);
76
77    /// Update the initial state to handle chdir() and making arguments absolute.
78    fn update(&mut self) {
79        self.get_color_mut().update();
80
81        if let Some(ref config) = self.get_config() {
82            if config.exists() {
83                self.set_config(path::abspath(config));
84            }
85        }
86        if let Some(ref root) = self.get_root() {
87            self.set_root(path::abspath(root));
88        }
89        if let Some(ref chdir) = self.get_chdir() {
90            if let Err(err) = std::env::set_current_dir(chdir) {
91                error!("could not chdir to {:?}: {}", chdir, err);
92            }
93        }
94    }
95
96    /// Return the debug level for the given name.
97    fn debug_level(&self, name: &str) -> u8 {
98        debug_level(self.get_debug(), name)
99    }
100}
101
102impl GardenOptions for MainOptions {
103    fn get_color_mut(&mut self) -> &mut model::ColorMode {
104        &mut self.color
105    }
106
107    fn get_config(&self) -> &Option<std::path::PathBuf> {
108        &self.config
109    }
110
111    fn set_config(&mut self, path: std::path::PathBuf) {
112        self.config = Some(path);
113    }
114
115    fn get_chdir(&self) -> &Option<std::path::PathBuf> {
116        &self.chdir
117    }
118
119    fn get_debug(&self) -> &[String] {
120        &self.debug
121    }
122
123    fn get_root(&self) -> &Option<std::path::PathBuf> {
124        &self.root
125    }
126
127    fn set_root(&mut self, root: std::path::PathBuf) {
128        self.root = Some(root);
129    }
130}
131
132/// Arguments to forward to custom commands
133#[derive(Default, Clone, Debug, Parser)]
134pub struct Arguments {
135    #[arg(allow_hyphen_values = true)]
136    pub args: Vec<String>,
137}
138
139#[derive(Clone, Debug, Subcommand)]
140pub enum Command {
141    /// Run custom commands over gardens
142    Cmd(cmds::cmd::CmdOptions),
143    /// Generate shell completions
144    Completion(cmds::completion::CompletionOptions),
145    /// Custom commands
146    #[command(external_subcommand)]
147    Custom(Vec<String>),
148    /// Evaluate garden expressions
149    Eval(cmds::eval::EvalOptions),
150    /// Run commands inside garden environments
151    Exec(cmds::exec::ExecOptions),
152    /// Grow garden worktrees into existence
153    Grow(cmds::grow::GrowOptions),
154    /// Garden GUI (run "garden-gui --help" for more details)
155    Gui(Arguments),
156    /// Initialize a "garden.yaml" garden configuration file
157    Init(cmds::init::InitOptions),
158    /// List available gardens, groups, trees and commands
159    #[command(name = "ls")]
160    List(cmds::list::ListOptions),
161    /// Add pre-existing worktrees to a garden configuration file
162    Plant(cmds::plant::PlantOptions),
163    /// Remove unreferenced Git repositories
164    Prune(cmds::prune::PruneOptions),
165    /// Open a shell in a garden environment
166    #[command(alias = "sh")]
167    Shell(cmds::shell::ShellOptions),
168}
169
170impl std::default::Default for Command {
171    fn default() -> Self {
172        Command::Custom(vec![])
173    }
174}
175
176/// Convert a verbose u8 into the corresponding command-line argument.
177pub fn verbose_string(verbose: u8) -> String {
178    let mut verbose_str = "-".to_string();
179    verbose_str.reserve((verbose + 1) as usize);
180    for _ in 0..verbose {
181        verbose_str.push('v');
182    }
183    verbose_str
184}