• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::collections::BTreeMap;
2 
3 use clap::{arg, command, ArgGroup, ArgMatches, Command};
4 
main()5 fn main() {
6     let matches = cli().get_matches();
7     let values = Value::from_matches(&matches);
8     println!("{:#?}", values);
9 }
10 
cli() -> Command11 fn 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)]
28 pub enum Value {
29     Bool(bool),
30     String(String),
31 }
32 
33 impl Value {
from_matches(matches: &ArgMatches) -> Vec<(clap::Id, Self)>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 
extract<T: Clone + Into<Value> + Send + Sync + 'static>( matches: &ArgMatches, id: &clap::Id, output: &mut BTreeMap<usize, (clap::Id, Self)>, ) -> bool59     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 
89 impl From<String> for Value {
from(other: String) -> Self90     fn from(other: String) -> Self {
91         Self::String(other)
92     }
93 }
94 
95 impl From<bool> for Value {
from(other: bool) -> Self96     fn from(other: bool) -> Self {
97         Self::Bool(other)
98     }
99 }
100