Skip to main content

cli_forge/
matches.rs

1//! The parsed result.
2//!
3//! A [`Matches`] is what the parser produces for one command level: the flags
4//! that were set, the values that options and positionals received, and — if a
5//! subcommand was invoked — the [`Matches`] for that subcommand, nested. A
6//! command's `run` handler receives the `Matches` for its own level.
7
8use std::collections::{HashMap, HashSet};
9
10/// Parsed arguments for one command level.
11///
12/// Read flags with [`flag`](Matches::flag), option and positional values with
13/// [`value`](Matches::value), and descend into an invoked subcommand with
14/// [`subcommand`](Matches::subcommand).
15///
16/// # Examples
17///
18/// ```
19/// use cli_forge::{App, Arg, Command};
20///
21/// let mut app = App::new("demo");
22/// app.register(
23///     Command::new("build")
24///         .arg(Arg::flag("release").short('r'))
25///         .arg(Arg::option("jobs").short('j').default("1")),
26/// );
27///
28/// let matches = app.try_parse_from(["build", "-r", "--jobs", "8"]).unwrap();
29/// let (name, build) = matches.subcommand().unwrap();
30/// assert_eq!(name, "build");
31/// assert!(build.flag("release"));
32/// assert_eq!(build.value("jobs"), Some("8"));
33/// ```
34#[derive(Clone, Debug, Default)]
35pub struct Matches {
36    pub(crate) flags: HashSet<String>,
37    pub(crate) values: HashMap<String, String>,
38    pub(crate) subcommand: Option<(String, Box<Matches>)>,
39}
40
41impl Matches {
42    /// Whether the flag named `name` was set.
43    ///
44    /// Returns `false` for an unset flag or an unknown name.
45    ///
46    /// # Examples
47    ///
48    /// ```
49    /// use cli_forge::{App, Arg, Command};
50    ///
51    /// let mut app = App::new("demo");
52    /// app.register(Command::new("run").arg(Arg::flag("verbose").short('v')));
53    ///
54    /// let m = app.try_parse_from(["run", "-v"]).unwrap();
55    /// assert!(m.subcommand().unwrap().1.flag("verbose"));
56    /// ```
57    #[must_use]
58    pub fn flag(&self, name: &str) -> bool {
59        self.flags.contains(name)
60    }
61
62    /// The value given for an option or positional named `name`, or its default.
63    ///
64    /// Returns `None` if the argument was not provided and has no default, or if
65    /// the name is unknown.
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// use cli_forge::{App, Arg, Command};
71    ///
72    /// let mut app = App::new("demo");
73    /// app.register(Command::new("greet").arg(Arg::positional("name").default("world")));
74    ///
75    /// let provided = app.try_parse_from(["greet", "Ada"]).unwrap();
76    /// assert_eq!(provided.subcommand().unwrap().1.value("name"), Some("Ada"));
77    ///
78    /// let defaulted = app.try_parse_from(["greet"]).unwrap();
79    /// assert_eq!(defaulted.subcommand().unwrap().1.value("name"), Some("world"));
80    /// ```
81    #[must_use]
82    pub fn value(&self, name: &str) -> Option<&str> {
83        self.values.get(name).map(String::as_str)
84    }
85
86    /// The invoked subcommand's name and its own [`Matches`], if one was given.
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// use cli_forge::{App, Command};
92    ///
93    /// let mut app = App::new("demo");
94    /// app.register(Command::new("status"));
95    ///
96    /// let m = app.try_parse_from(["status"]).unwrap();
97    /// assert_eq!(m.subcommand().map(|(name, _)| name), Some("status"));
98    /// ```
99    #[must_use]
100    pub fn subcommand(&self) -> Option<(&str, &Matches)> {
101        self.subcommand
102            .as_ref()
103            .map(|(name, matches)| (name.as_str(), matches.as_ref()))
104    }
105}