fork_manager/
cli.rs

1use super::{Error, Result};
2use clap::CommandFactory;
3use std::path::{Path, PathBuf};
4
5#[derive(clap::Parser, Debug)]
6#[command(version, about, long_about = None)]
7pub struct Args {
8    /// Path to the configuration file.
9    /// If not given, or not a file, this will be searched
10    /// according to arguments "project" and "filename"
11    #[arg(
12        short,
13        long,
14        env = "FORK_MANAGER_CONFIG_FILE",
15        default_value = "./fork-manager.yaml",
16        value_hint = clap::ValueHint::FilePath,
17    )]
18    pub config_file: PathBuf,
19
20    /// Name of the configuration file to look for
21    #[arg(
22        short,
23        long,
24        env = "FORK_MANAGER_CONFIG_FILENAME",
25        default_value = "fork-manager.yaml",
26        value_hint = clap::ValueHint::FilePath,
27    )]
28    pub filename: PathBuf,
29
30    /// Path to the project where to look for
31    #[arg(
32        short,
33        long,
34        env = "FORK_MANAGER_PROJECT",
35        default_value = ".",
36        value_hint = clap::ValueHint::DirPath
37    )]
38    pub project: PathBuf,
39
40    /// Name of the script to generate
41    #[arg(
42        short,
43        long,
44        env = "FORK_MANAGER_UPDATE_SCRIPT",
45        default_value = "update.sh",
46        value_hint = clap::ValueHint::FilePath,
47    )]
48    pub update_script: PathBuf,
49
50    /// If provided, outputs the completion file for given shell and exit
51    #[arg(long = "generate", value_enum)]
52    pub generator: Option<clap_complete::Shell>,
53
54    /// Only check config, don't run git commands
55    #[arg(short, long)]
56    pub dry_run: bool,
57}
58
59impl Args {
60    pub fn process(&mut self) -> Result<bool> {
61        if let Some(generator) = self.generator {
62            let mut cmd = Args::command();
63            print_completions(generator, &mut cmd);
64            Ok(false)
65        } else {
66            if !self.config_file.is_file() {
67                self.config_file = find_config_file(&self.project, &self.filename)?;
68            }
69
70            // now that we have a proper canonical config_file, we can ensure project root
71            for ancestor in self.config_file.ancestors() {
72                if self.config_file == ancestor.join(&self.filename) {
73                    self.project = ancestor.to_path_buf();
74                }
75            }
76            Ok(true)
77        }
78    }
79}
80
81pub fn print_completions<G: clap_complete::Generator>(gen: G, cmd: &mut clap::Command) {
82    clap_complete::generate(gen, cmd, "fork-manager", &mut std::io::stdout());
83}
84
85fn find_config_file(project: &Path, filename: &Path) -> Result<PathBuf> {
86    let mut dir = project.canonicalize()?;
87    let mut path;
88    loop {
89        path = dir.join(filename);
90        if path.is_file() {
91            return Ok(path);
92        }
93        dir = dir
94            .parent()
95            .ok_or(Error::NotFound(project.to_path_buf()))?
96            .to_path_buf();
97    }
98}