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}