1 // Std
2 use std::{collections::HashMap, ffi::OsString, mem, ops::Deref};
3 
4 // Internal
5 use crate::{
6     build::{App, Arg, ArgSettings},
7     parse::{ArgMatches, MatchedArg, SubCommand, ValueType},
8     util::Id,
9 };
10 
11 // Third party
12 use indexmap::map::Entry;
13 
14 #[derive(Debug, Default)]
15 pub(crate) struct ArgMatcher(ArgMatches);
16 
17 impl ArgMatcher {
new(_app: &App) -> Self18     pub(crate) fn new(_app: &App) -> Self {
19         ArgMatcher(ArgMatches {
20             #[cfg(debug_assertions)]
21             valid_args: {
22                 let args = _app.args.args().map(|a| a.id.clone());
23                 let groups = _app.groups.iter().map(|g| g.id.clone());
24                 args.chain(groups).collect()
25             },
26             #[cfg(debug_assertions)]
27             valid_subcommands: _app.subcommands.iter().map(|sc| sc.id.clone()).collect(),
28             // HACK: Allow an external subcommand's ArgMatches be a stand-in for any ArgMatches
29             // since users can't detect it and avoid the asserts.
30             //
31             // See clap-rs/clap#3263
32             #[cfg(debug_assertions)]
33             disable_asserts: _app.is_set(crate::AppSettings::AllowExternalSubcommands),
34             ..Default::default()
35         })
36     }
37 
into_inner(self) -> ArgMatches38     pub(crate) fn into_inner(self) -> ArgMatches {
39         self.0
40     }
41 
propagate_globals(&mut self, global_arg_vec: &[Id])42     pub(crate) fn propagate_globals(&mut self, global_arg_vec: &[Id]) {
43         debug!(
44             "ArgMatcher::get_global_values: global_arg_vec={:?}",
45             global_arg_vec
46         );
47         let mut vals_map = HashMap::new();
48         self.fill_in_global_values(global_arg_vec, &mut vals_map);
49     }
50 
fill_in_global_values( &mut self, global_arg_vec: &[Id], vals_map: &mut HashMap<Id, MatchedArg>, )51     fn fill_in_global_values(
52         &mut self,
53         global_arg_vec: &[Id],
54         vals_map: &mut HashMap<Id, MatchedArg>,
55     ) {
56         for global_arg in global_arg_vec {
57             if let Some(ma) = self.get(global_arg) {
58                 // We have to check if the parent's global arg wasn't used but still exists
59                 // such as from a default value.
60                 //
61                 // For example, `myprog subcommand --global-arg=value` where --global-arg defines
62                 // a default value of `other` myprog would have an existing MatchedArg for
63                 // --global-arg where the value is `other`, however the occurs will be 0.
64                 let to_update = if let Some(parent_ma) = vals_map.get(global_arg) {
65                     if parent_ma.get_occurrences() > 0 && ma.get_occurrences() == 0 {
66                         parent_ma
67                     } else {
68                         ma
69                     }
70                 } else {
71                     ma
72                 }
73                 .clone();
74                 vals_map.insert(global_arg.clone(), to_update);
75             }
76         }
77         if let Some(ref mut sc) = self.0.subcommand {
78             let mut am = ArgMatcher(mem::take(&mut sc.matches));
79             am.fill_in_global_values(global_arg_vec, vals_map);
80             mem::swap(&mut am.0, &mut sc.matches);
81         }
82 
83         for (name, matched_arg) in vals_map.iter_mut() {
84             self.0.args.insert(name.clone(), matched_arg.clone());
85         }
86     }
87 
get_mut(&mut self, arg: &Id) -> Option<&mut MatchedArg>88     pub(crate) fn get_mut(&mut self, arg: &Id) -> Option<&mut MatchedArg> {
89         self.0.args.get_mut(arg)
90     }
91 
get(&self, arg: &Id) -> Option<&MatchedArg>92     pub(crate) fn get(&self, arg: &Id) -> Option<&MatchedArg> {
93         self.0.args.get(arg)
94     }
95 
remove(&mut self, arg: &Id)96     pub(crate) fn remove(&mut self, arg: &Id) {
97         self.0.args.swap_remove(arg);
98     }
99 
contains(&self, arg: &Id) -> bool100     pub(crate) fn contains(&self, arg: &Id) -> bool {
101         self.0.args.contains_key(arg)
102     }
103 
contains_explicit(&self, arg: &Id) -> bool104     pub(crate) fn contains_explicit(&self, arg: &Id) -> bool {
105         self.0
106             .args
107             .get(arg)
108             .map_or(false, |a| a.source() != ValueType::DefaultValue)
109     }
110 
is_empty(&self) -> bool111     pub(crate) fn is_empty(&self) -> bool {
112         self.0.args.is_empty()
113     }
114 
arg_names(&self) -> indexmap::map::Keys<Id, MatchedArg>115     pub(crate) fn arg_names(&self) -> indexmap::map::Keys<Id, MatchedArg> {
116         self.0.args.keys()
117     }
118 
entry(&mut self, arg: &Id) -> indexmap::map::Entry<Id, MatchedArg>119     pub(crate) fn entry(&mut self, arg: &Id) -> indexmap::map::Entry<Id, MatchedArg> {
120         self.0.args.entry(arg.clone())
121     }
122 
subcommand(&mut self, sc: SubCommand)123     pub(crate) fn subcommand(&mut self, sc: SubCommand) {
124         self.0.subcommand = Some(Box::new(sc));
125     }
126 
subcommand_name(&self) -> Option<&str>127     pub(crate) fn subcommand_name(&self) -> Option<&str> {
128         self.0.subcommand_name()
129     }
130 
iter(&self) -> indexmap::map::Iter<Id, MatchedArg>131     pub(crate) fn iter(&self) -> indexmap::map::Iter<Id, MatchedArg> {
132         self.0.args.iter()
133     }
134 
inc_occurrence_of_arg(&mut self, arg: &Arg)135     pub(crate) fn inc_occurrence_of_arg(&mut self, arg: &Arg) {
136         let id = &arg.id;
137         debug!("ArgMatcher::inc_occurrence_of_arg: id={:?}", id);
138         let ma = self.entry(id).or_insert(MatchedArg::new());
139         ma.update_ty(ValueType::CommandLine);
140         ma.set_ignore_case(arg.is_set(ArgSettings::IgnoreCase));
141         ma.invalid_utf8_allowed(arg.is_set(ArgSettings::AllowInvalidUtf8));
142         ma.inc_occurrences();
143     }
144 
inc_occurrence_of_group(&mut self, id: &Id)145     pub(crate) fn inc_occurrence_of_group(&mut self, id: &Id) {
146         debug!("ArgMatcher::inc_occurrence_of_group: id={:?}", id);
147         let ma = self.entry(id).or_insert(MatchedArg::new());
148         ma.update_ty(ValueType::CommandLine);
149         ma.inc_occurrences();
150     }
151 
add_val_to(&mut self, arg: &Id, val: OsString, ty: ValueType, append: bool)152     pub(crate) fn add_val_to(&mut self, arg: &Id, val: OsString, ty: ValueType, append: bool) {
153         if append {
154             self.append_val_to(arg, val, ty);
155         } else {
156             self.push_val_to(arg, val, ty);
157         }
158     }
159 
push_val_to(&mut self, arg: &Id, val: OsString, ty: ValueType)160     fn push_val_to(&mut self, arg: &Id, val: OsString, ty: ValueType) {
161         // We will manually inc occurrences later(for flexibility under
162         // specific circumstances, like only add one occurrence for flag
163         // when we met: `--flag=one,two`).
164         let ma = self.entry(arg).or_default();
165         ma.update_ty(ty);
166         ma.push_val(val);
167     }
168 
append_val_to(&mut self, arg: &Id, val: OsString, ty: ValueType)169     fn append_val_to(&mut self, arg: &Id, val: OsString, ty: ValueType) {
170         let ma = self.entry(arg).or_default();
171         ma.update_ty(ty);
172         ma.append_val(val);
173     }
174 
new_val_group(&mut self, arg: &Id)175     pub(crate) fn new_val_group(&mut self, arg: &Id) {
176         let ma = self.entry(arg).or_default();
177         ma.new_val_group();
178     }
179 
add_index_to(&mut self, arg: &Id, idx: usize, ty: ValueType)180     pub(crate) fn add_index_to(&mut self, arg: &Id, idx: usize, ty: ValueType) {
181         let ma = self.entry(arg).or_default();
182         ma.update_ty(ty);
183         ma.push_index(idx);
184     }
185 
has_val_groups(&mut self, arg: &Id) -> bool186     pub(crate) fn has_val_groups(&mut self, arg: &Id) -> bool {
187         match self.entry(arg) {
188             Entry::Occupied(e) => e.get().has_val_groups(),
189             Entry::Vacant(_) => false,
190         }
191     }
192 
needs_more_vals(&self, o: &Arg) -> bool193     pub(crate) fn needs_more_vals(&self, o: &Arg) -> bool {
194         debug!("ArgMatcher::needs_more_vals: o={}", o.name);
195         if let Some(ma) = self.get(&o.id) {
196             let current_num = ma.num_vals();
197             if let Some(num) = o.num_vals {
198                 debug!("ArgMatcher::needs_more_vals: num_vals...{}", num);
199                 return if o.is_set(ArgSettings::MultipleOccurrences) {
200                     (current_num % num) != 0
201                 } else {
202                     num != current_num
203                 };
204             } else if let Some(num) = o.max_vals {
205                 debug!("ArgMatcher::needs_more_vals: max_vals...{}", num);
206                 return current_num < num;
207             } else if o.min_vals.is_some() {
208                 debug!("ArgMatcher::needs_more_vals: min_vals...true");
209                 return true;
210             }
211             return o.is_set(ArgSettings::MultipleValues);
212         }
213         true
214     }
215 }
216 
217 impl Deref for ArgMatcher {
218     type Target = ArgMatches;
219 
deref(&self) -> &Self::Target220     fn deref(&self) -> &Self::Target {
221         &self.0
222     }
223 }
224