halp/
cli.rs

1use crate::config::Config;
2use clap::{Parser, Subcommand};
3use std::path::PathBuf;
4
5/// Command-line arguments.
6#[derive(Debug, Default, Parser)]
7#[command(
8version,
9author,
10about,
11subcommand_negates_reqs = true,
12disable_help_subcommand = true,
13override_usage = format!("
14  {bin} [OPTIONS] <CMD>
15  {bin} [OPTIONS] <COMMAND> <CMD>", bin = env!("CARGO_PKG_NAME"))
16)]
17pub struct CliArgs {
18    /// Command or binary name.
19    #[arg(required = true)]
20    pub cmd: Option<String>,
21    /// Sets the argument to check.
22    #[arg(long = "check", value_name = "ARG", value_parser = CliArgs::parse_arg)]
23    pub check_args: Option<Vec<String>>,
24    /// Disable checking the version information.
25    #[arg(long)]
26    pub no_version: bool,
27    /// Disable checking the help information.
28    #[arg(long)]
29    pub no_help: bool,
30    /// Sets the configuration file.
31    #[arg(short, long, env = "HALP_CONFIG", value_name = "PATH")]
32    pub config: Option<PathBuf>,
33    /// Sets the timeout for the command.
34    #[arg(short, long, value_name = "S")]
35    pub timeout: Option<u64>,
36    /// Enables verbose logging.
37    #[arg(short, long)]
38    pub verbose: bool,
39    /// Subcommands.
40    #[command(subcommand)]
41    pub subcommand: Option<CliCommands>,
42}
43
44/// Subcommands.
45#[derive(Debug, Subcommand)]
46pub enum CliCommands {
47    /// Get additional help.
48    Plz {
49        /// Command or binary name.
50        cmd: String,
51        /// Sets the manual page command to run.
52        #[arg(short, long)]
53        man_cmd: Option<String>,
54        /// Use a custom URL for cheat.sh.
55        #[arg(long, env = "CHEAT_SH_URL", value_name = "URL")]
56        cheat_sh_url: Option<String>,
57        /// Use a custom provider URL for `eg` pages.
58        #[arg(long, env = "EG_PAGES_URL", value_name = "URL")]
59        eg_url: Option<String>,
60        /// Use a custom URL for cheat sheets.
61        #[arg(long, env = "CHEATSHEETS_URL", value_name = "URL")]
62        cheat_url: Option<String>,
63        /// Sets the pager to use.
64        #[arg(short, long)]
65        pager: Option<String>,
66        /// Disables the pager.
67        #[arg(long)]
68        no_pager: bool,
69    },
70}
71
72impl CliArgs {
73    /// Custom argument parser for escaping the '-' character.
74    fn parse_arg(arg: &str) -> Result<String, String> {
75        Ok(arg.replace("\\-", "-"))
76    }
77
78    /// Update the configuration based on the command-line arguments (the command-line arguments will override the configuration).
79    pub fn update_config(&self, config: &mut Config) {
80        config.check_help = !self.no_help;
81        config.check_version = !self.no_version;
82        if let Some(ref args) = self.check_args {
83            config.check_args = Some(args.iter().map(|s| vec![s.to_string()]).collect());
84        }
85        if self.timeout.is_some() {
86            config.timeout = self.timeout;
87        }
88        if let Some(CliCommands::Plz {
89            ref man_cmd,
90            ref cheat_sh_url,
91            ref eg_url,
92            no_pager,
93            ref pager,
94            ..
95        }) = self.subcommand
96        {
97            if let Some(man_cmd) = man_cmd {
98                config.man_command.clone_from(man_cmd);
99            }
100            if let Some(cheat_sh_url) = cheat_sh_url {
101                config.cheat_sh_url = Some(cheat_sh_url.clone());
102            }
103            if let Some(eg_url) = eg_url {
104                config.eg_url = Some(eg_url.to_owned());
105            }
106            if no_pager {
107                config.pager_command = None;
108            } else if let Some(pager) = pager {
109                config.pager_command = Some(pager.clone());
110            }
111        }
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118    use clap::CommandFactory;
119    use pretty_assertions::assert_eq;
120
121    #[test]
122    fn test_cli_args() {
123        CliArgs::command().debug_assert();
124        assert_eq!(Ok("--help"), CliArgs::parse_arg("\\--help").as_deref());
125    }
126
127    #[test]
128    fn test_update_config() {
129        let mut config = Config::default();
130        let args = CliArgs {
131            subcommand: Some(CliCommands::Plz {
132                cmd: "ps".to_string(),
133                pager: Some("bat".to_string()),
134                cheat_sh_url: None,
135                cheat_url: None,
136                eg_url: None,
137                man_cmd: None,
138                no_pager: false,
139            }),
140            ..Default::default()
141        };
142        args.update_config(&mut config);
143        assert!(config.check_help);
144        assert_eq!(Some(String::from("bat")), config.pager_command);
145    }
146}