1 | // Std |
2 | use std::{ |
3 | ffi::{OsStr, OsString}, |
4 | iter::{Cloned, Flatten}, |
5 | slice::Iter, |
6 | }; |
7 | |
8 | use crate::builder::ArgPredicate; |
9 | use crate::parser::ValueSource; |
10 | use crate::util::eq_ignore_case; |
11 | use crate::util::AnyValue; |
12 | use crate::util::AnyValueId; |
13 | use crate::INTERNAL_ERROR_MSG; |
14 | |
15 | #[derive (Debug, Clone)] |
16 | pub(crate) struct MatchedArg { |
17 | source: Option<ValueSource>, |
18 | indices: Vec<usize>, |
19 | type_id: Option<AnyValueId>, |
20 | vals: Vec<Vec<AnyValue>>, |
21 | raw_vals: Vec<Vec<OsString>>, |
22 | ignore_case: bool, |
23 | } |
24 | |
25 | impl MatchedArg { |
26 | pub(crate) fn new_arg(arg: &crate::Arg) -> Self { |
27 | let ignore_case = arg.is_ignore_case_set(); |
28 | Self { |
29 | source: None, |
30 | indices: Vec::new(), |
31 | type_id: Some(arg.get_value_parser().type_id()), |
32 | vals: Vec::new(), |
33 | raw_vals: Vec::new(), |
34 | ignore_case, |
35 | } |
36 | } |
37 | |
38 | pub(crate) fn new_group() -> Self { |
39 | let ignore_case = false; |
40 | Self { |
41 | source: None, |
42 | indices: Vec::new(), |
43 | type_id: None, |
44 | vals: Vec::new(), |
45 | raw_vals: Vec::new(), |
46 | ignore_case, |
47 | } |
48 | } |
49 | |
50 | pub(crate) fn new_external(cmd: &crate::Command) -> Self { |
51 | let ignore_case = false; |
52 | Self { |
53 | source: None, |
54 | indices: Vec::new(), |
55 | type_id: Some( |
56 | cmd.get_external_subcommand_value_parser() |
57 | .expect(INTERNAL_ERROR_MSG) |
58 | .type_id(), |
59 | ), |
60 | vals: Vec::new(), |
61 | raw_vals: Vec::new(), |
62 | ignore_case, |
63 | } |
64 | } |
65 | |
66 | pub(crate) fn indices(&self) -> Cloned<Iter<'_, usize>> { |
67 | self.indices.iter().cloned() |
68 | } |
69 | |
70 | pub(crate) fn get_index(&self, index: usize) -> Option<usize> { |
71 | self.indices.get(index).cloned() |
72 | } |
73 | |
74 | pub(crate) fn push_index(&mut self, index: usize) { |
75 | self.indices.push(index) |
76 | } |
77 | |
78 | pub(crate) fn vals(&self) -> Iter<Vec<AnyValue>> { |
79 | self.vals.iter() |
80 | } |
81 | |
82 | pub(crate) fn into_vals(self) -> Vec<Vec<AnyValue>> { |
83 | self.vals |
84 | } |
85 | |
86 | pub(crate) fn vals_flatten(&self) -> Flatten<Iter<Vec<AnyValue>>> { |
87 | self.vals.iter().flatten() |
88 | } |
89 | |
90 | pub(crate) fn into_vals_flatten(self) -> Flatten<std::vec::IntoIter<Vec<AnyValue>>> { |
91 | self.vals.into_iter().flatten() |
92 | } |
93 | |
94 | pub(crate) fn raw_vals(&self) -> Iter<Vec<OsString>> { |
95 | self.raw_vals.iter() |
96 | } |
97 | |
98 | pub(crate) fn raw_vals_flatten(&self) -> Flatten<Iter<Vec<OsString>>> { |
99 | self.raw_vals.iter().flatten() |
100 | } |
101 | |
102 | pub(crate) fn first(&self) -> Option<&AnyValue> { |
103 | self.vals_flatten().next() |
104 | } |
105 | |
106 | #[cfg (test)] |
107 | pub(crate) fn first_raw(&self) -> Option<&OsString> { |
108 | self.raw_vals_flatten().next() |
109 | } |
110 | |
111 | pub(crate) fn new_val_group(&mut self) { |
112 | self.vals.push(vec![]); |
113 | self.raw_vals.push(vec![]); |
114 | } |
115 | |
116 | pub(crate) fn append_val(&mut self, val: AnyValue, raw_val: OsString) { |
117 | // We assume there is always a group created before. |
118 | self.vals.last_mut().expect(INTERNAL_ERROR_MSG).push(val); |
119 | self.raw_vals |
120 | .last_mut() |
121 | .expect(INTERNAL_ERROR_MSG) |
122 | .push(raw_val); |
123 | } |
124 | |
125 | pub(crate) fn num_vals(&self) -> usize { |
126 | self.vals.iter().map(|v| v.len()).sum() |
127 | } |
128 | |
129 | // Will be used later |
130 | #[allow (dead_code)] |
131 | pub(crate) fn num_vals_last_group(&self) -> usize { |
132 | self.vals.last().map(|x| x.len()).unwrap_or(0) |
133 | } |
134 | |
135 | pub(crate) fn all_val_groups_empty(&self) -> bool { |
136 | self.vals.iter().flatten().count() == 0 |
137 | } |
138 | |
139 | pub(crate) fn check_explicit(&self, predicate: &ArgPredicate) -> bool { |
140 | if self.source.map(|s| !s.is_explicit()).unwrap_or(false) { |
141 | return false; |
142 | } |
143 | |
144 | match predicate { |
145 | ArgPredicate::Equals(val) => self.raw_vals_flatten().any(|v| { |
146 | if self.ignore_case { |
147 | // If `v` isn't utf8, it can't match `val`, so `OsStr::to_str` should be fine |
148 | eq_ignore_case(&v.to_string_lossy(), &val.to_string_lossy()) |
149 | } else { |
150 | OsString::as_os_str(v) == OsStr::new(val) |
151 | } |
152 | }), |
153 | ArgPredicate::IsPresent => true, |
154 | } |
155 | } |
156 | |
157 | pub(crate) fn source(&self) -> Option<ValueSource> { |
158 | self.source |
159 | } |
160 | |
161 | pub(crate) fn set_source(&mut self, source: ValueSource) { |
162 | if let Some(existing) = self.source { |
163 | self.source = Some(existing.max(source)); |
164 | } else { |
165 | self.source = Some(source) |
166 | } |
167 | } |
168 | |
169 | pub(crate) fn type_id(&self) -> Option<AnyValueId> { |
170 | self.type_id |
171 | } |
172 | |
173 | pub(crate) fn infer_type_id(&self, expected: AnyValueId) -> AnyValueId { |
174 | self.type_id() |
175 | .or_else(|| { |
176 | self.vals_flatten() |
177 | .map(|v| v.type_id()) |
178 | .find(|actual| *actual != expected) |
179 | }) |
180 | .unwrap_or(expected) |
181 | } |
182 | } |
183 | |
184 | impl PartialEq for MatchedArg { |
185 | fn eq(&self, other: &MatchedArg) -> bool { |
186 | let MatchedArg { |
187 | source: self_source, |
188 | indices: self_indices, |
189 | type_id: self_type_id, |
190 | vals: _, |
191 | raw_vals: self_raw_vals, |
192 | ignore_case: self_ignore_case, |
193 | } = self; |
194 | let MatchedArg { |
195 | source: other_source, |
196 | indices: other_indices, |
197 | type_id: other_type_id, |
198 | vals: _, |
199 | raw_vals: other_raw_vals, |
200 | ignore_case: other_ignore_case, |
201 | } = other; |
202 | self_source == other_source |
203 | && self_indices == other_indices |
204 | && self_type_id == other_type_id |
205 | && self_raw_vals == other_raw_vals |
206 | && self_ignore_case == other_ignore_case |
207 | } |
208 | } |
209 | |
210 | impl Eq for MatchedArg {} |
211 | |
212 | #[cfg (test)] |
213 | mod tests { |
214 | use super::*; |
215 | |
216 | #[test ] |
217 | fn test_grouped_vals_first() { |
218 | let mut m = MatchedArg::new_group(); |
219 | m.new_val_group(); |
220 | m.new_val_group(); |
221 | m.append_val(AnyValue::new(String::from("bbb" )), "bbb" .into()); |
222 | m.append_val(AnyValue::new(String::from("ccc" )), "ccc" .into()); |
223 | assert_eq!(m.first_raw(), Some(&OsString::from("bbb" ))); |
224 | } |
225 | } |
226 | |