Skip to main content

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