xref: /third_party/rust/crates/clap/examples/find.rs (revision 19625d8c)
1use std::collections::BTreeMap;
2
3use clap::{arg, command, ArgGroup, ArgMatches, Command};
4
5fn main() {
6    let matches = cli().get_matches();
7    let values = Value::from_matches(&matches);
8    println!("{:#?}", values);
9}
10
11fn cli() -> Command {
12    command!()
13        .group(ArgGroup::new("tests").multiple(true))
14        .next_help_heading("TESTS")
15        .args([
16            arg!(--empty "File is empty and is either a regular file or a directory").group("tests"),
17            arg!(--name <NAME> "Base of file name (the path with the leading directories removed) matches shell pattern pattern").group("tests"),
18        ])
19        .group(ArgGroup::new("operators").multiple(true))
20        .next_help_heading("OPERATORS")
21        .args([
22            arg!(-o - -or "expr2 is not evaluate if exp1 is true").group("operators"),
23            arg!(-a - -and "Same as `expr1 expr1`").group("operators"),
24        ])
25}
26
27#[derive(Clone, PartialEq, Eq, Hash, Debug)]
28pub enum Value {
29    Bool(bool),
30    String(String),
31}
32
33impl Value {
34    pub fn from_matches(matches: &ArgMatches) -> Vec<(clap::Id, Self)> {
35        let mut values = BTreeMap::new();
36        for id in matches.ids() {
37            if matches.try_get_many::<clap::Id>(id.as_str()).is_ok() {
38                // ignore groups
39                continue;
40            }
41            let value_source = matches
42                .value_source(id.as_str())
43                .expect("id came from matches");
44            if value_source != clap::parser::ValueSource::CommandLine {
45                // Any other source just gets tacked on at the end (like default values)
46                continue;
47            }
48            if Self::extract::<String>(matches, id, &mut values) {
49                continue;
50            }
51            if Self::extract::<bool>(matches, id, &mut values) {
52                continue;
53            }
54            unimplemented!("unknown type for {}: {:?}", id, matches);
55        }
56        values.into_values().collect::<Vec<_>>()
57    }
58
59    fn extract<T: Clone + Into<Value> + Send + Sync + 'static>(
60        matches: &ArgMatches,
61        id: &clap::Id,
62        output: &mut BTreeMap<usize, (clap::Id, Self)>,
63    ) -> bool {
64        match matches.try_get_many::<T>(id.as_str()) {
65            Ok(Some(values)) => {
66                for (value, index) in values.zip(
67                    matches
68                        .indices_of(id.as_str())
69                        .expect("id came from matches"),
70                ) {
71                    output.insert(index, (id.clone(), value.clone().into()));
72                }
73                true
74            }
75            Ok(None) => {
76                unreachable!("`ids` only reports what is present")
77            }
78            Err(clap::parser::MatchesError::UnknownArgument { .. }) => {
79                unreachable!("id came from matches")
80            }
81            Err(clap::parser::MatchesError::Downcast { .. }) => false,
82            Err(_) => {
83                unreachable!("id came from matches")
84            }
85        }
86    }
87}
88
89impl From<String> for Value {
90    fn from(other: String) -> Self {
91        Self::String(other)
92    }
93}
94
95impl From<bool> for Value {
96    fn from(other: bool) -> Self {
97        Self::Bool(other)
98    }
99}
100