]>
Commit | Line | Data |
---|---|---|
cc61c64b XL |
1 | // std |
2 | use std::fmt::Display; | |
3 | ||
4 | // Internal | |
5 | use INTERNAL_ERROR_MSG; | |
6 | use INVALID_UTF8; | |
7 | use args::{AnyArg, ArgMatcher, MatchedArg}; | |
8 | use args::settings::ArgSettings; | |
9 | use errors::{Error, ErrorKind}; | |
10 | use errors::Result as ClapResult; | |
11 | use osstringext::OsStrExt2; | |
12 | use app::settings::AppSettings as AS; | |
13 | use app::parser::Parser; | |
14 | use fmt::Colorizer; | |
15 | use app::usage; | |
16 | ||
17 | pub struct Validator<'a, 'b, 'z>(&'z mut Parser<'a, 'b>) | |
18 | where 'a: 'b, | |
19 | 'b: 'z; | |
20 | ||
21 | impl<'a, 'b, 'z> Validator<'a, 'b, 'z> { | |
22 | pub fn new(p: &'z mut Parser<'a, 'b>) -> Self { Validator(p) } | |
23 | ||
24 | pub fn validate(&mut self, | |
25 | needs_val_of: Option<&'a str>, | |
26 | subcmd_name: Option<String>, | |
27 | matcher: &mut ArgMatcher<'a>) | |
28 | -> ClapResult<()> { | |
29 | debugln!("Validator::validate;"); | |
30 | let mut reqs_validated = false; | |
31 | try!(self.0.add_defaults(matcher)); | |
32 | if let Some(a) = needs_val_of { | |
33 | debugln!("Validator::validate: needs_val_of={:?}", a); | |
34 | if let Some(o) = find_by_name!(self.0, &a, opts, iter) { | |
35 | try!(self.validate_required(matcher)); | |
36 | reqs_validated = true; | |
37 | let should_err = if let Some(v) = matcher.0.args.get(&*o.b.name) { | |
38 | v.vals.is_empty() && !(o.v.min_vals.is_some() && o.v.min_vals.unwrap() == 0) | |
39 | } else { | |
40 | true | |
41 | }; | |
42 | if should_err { | |
43 | return Err(Error::empty_value(o, | |
44 | &*usage::create_error_usage(self.0, | |
45 | matcher, | |
46 | None), | |
47 | self.0.color())); | |
48 | } | |
49 | } | |
50 | } | |
51 | ||
52 | if matcher.is_empty() && matcher.subcommand_name().is_none() && | |
53 | self.0.is_set(AS::ArgRequiredElseHelp) { | |
54 | let mut out = vec![]; | |
55 | try!(self.0.write_help_err(&mut out)); | |
56 | return Err(Error { | |
57 | message: String::from_utf8_lossy(&*out).into_owned(), | |
58 | kind: ErrorKind::MissingArgumentOrSubcommand, | |
59 | info: None, | |
60 | }); | |
61 | } | |
62 | try!(self.validate_blacklist(matcher)); | |
63 | if !(self.0.is_set(AS::SubcommandsNegateReqs) && subcmd_name.is_some()) && !reqs_validated { | |
64 | try!(self.validate_required(matcher)); | |
65 | } | |
66 | try!(self.validate_matched_args(matcher)); | |
67 | matcher.usage(usage::create_usage_with_title(self.0, &[])); | |
68 | ||
69 | Ok(()) | |
70 | } | |
71 | ||
72 | fn validate_values<A>(&self, | |
73 | arg: &A, | |
74 | ma: &MatchedArg, | |
75 | matcher: &ArgMatcher<'a>) | |
76 | -> ClapResult<()> | |
77 | where A: AnyArg<'a, 'b> + Display | |
78 | { | |
79 | debugln!("Validator::validate_values: arg={:?}", arg.name()); | |
80 | for val in &ma.vals { | |
81 | if self.0.is_set(AS::StrictUtf8) && val.to_str().is_none() { | |
82 | debugln!("Validator::validate_values: invalid UTF-8 found in val {:?}", | |
83 | val); | |
84 | return Err(Error::invalid_utf8(&*usage::create_error_usage(self.0, matcher, None), | |
85 | self.0.color())); | |
86 | } | |
87 | if let Some(p_vals) = arg.possible_vals() { | |
88 | debugln!("Validator::validate_values: possible_vals={:?}", p_vals); | |
89 | let val_str = val.to_string_lossy(); | |
90 | if !p_vals.contains(&&*val_str) { | |
91 | return Err(Error::invalid_value(val_str, | |
92 | p_vals, | |
93 | arg, | |
94 | &*usage::create_error_usage(self.0, | |
95 | matcher, | |
96 | None), | |
97 | self.0.color())); | |
98 | } | |
99 | } | |
100 | if !arg.is_set(ArgSettings::EmptyValues) && val.is_empty_() && | |
101 | matcher.contains(&*arg.name()) { | |
102 | debugln!("Validator::validate_values: illegal empty val found"); | |
103 | return Err(Error::empty_value(arg, | |
104 | &*usage::create_error_usage(self.0, matcher, None), | |
105 | self.0.color())); | |
106 | } | |
107 | if let Some(vtor) = arg.validator() { | |
108 | debug!("Validator::validate_values: checking validator..."); | |
109 | if let Err(e) = vtor(val.to_string_lossy().into_owned()) { | |
110 | sdebugln!("error"); | |
111 | return Err(Error::value_validation(Some(arg), e, self.0.color())); | |
112 | } else { | |
113 | sdebugln!("good"); | |
114 | } | |
115 | } | |
116 | if let Some(vtor) = arg.validator_os() { | |
117 | debug!("Validator::validate_values: checking validator_os..."); | |
118 | if let Err(e) = vtor(val) { | |
119 | sdebugln!("error"); | |
120 | return Err(Error::value_validation(Some(arg), | |
121 | (*e).to_string_lossy().to_string(), | |
122 | self.0.color())); | |
123 | } else { | |
124 | sdebugln!("good"); | |
125 | } | |
126 | } | |
127 | } | |
128 | Ok(()) | |
129 | } | |
130 | ||
131 | fn validate_blacklist(&self, matcher: &mut ArgMatcher) -> ClapResult<()> { | |
132 | debugln!("Validator::validate_blacklist: blacklist={:?}", | |
133 | self.0.blacklist); | |
134 | macro_rules! build_err { | |
135 | ($p:expr, $name:expr, $matcher:ident) => ({ | |
136 | debugln!("build_err!: name={}", $name); | |
137 | let mut c_with = find_from!($p, $name, blacklist, &$matcher); | |
138 | c_with = c_with.or( | |
139 | $p.find_any_arg($name).map_or(None, |aa| aa.blacklist()) | |
140 | .map_or(None, | |
141 | |bl| bl.iter().find(|arg| $matcher.contains(arg))) | |
142 | .map_or(None, |an| $p.find_any_arg(an)) | |
143 | .map_or(None, |aa| Some(format!("{}", aa))) | |
144 | ); | |
145 | debugln!("build_err!: '{:?}' conflicts with '{}'", c_with, $name); | |
146 | $matcher.remove($name); | |
147 | let usg = usage::create_error_usage($p, $matcher, None); | |
148 | if let Some(f) = find_by_name!($p, $name, flags, iter) { | |
149 | debugln!("build_err!: It was a flag..."); | |
150 | Error::argument_conflict(f, c_with, &*usg, self.0.color()) | |
151 | } else if let Some(o) = find_by_name!($p, $name, opts, iter) { | |
152 | debugln!("build_err!: It was an option..."); | |
153 | Error::argument_conflict(o, c_with, &*usg, self.0.color()) | |
154 | } else { | |
155 | match find_by_name!($p, $name, positionals, values) { | |
156 | Some(p) => { | |
157 | debugln!("build_err!: It was a positional..."); | |
158 | Error::argument_conflict(p, c_with, &*usg, self.0.color()) | |
159 | }, | |
160 | None => panic!(INTERNAL_ERROR_MSG) | |
161 | } | |
162 | } | |
163 | }); | |
164 | } | |
165 | ||
166 | for name in &self.0.blacklist { | |
167 | debugln!("Validator::validate_blacklist:iter: Checking blacklisted name: {}", | |
168 | name); | |
169 | if self.0 | |
170 | .groups | |
171 | .iter() | |
172 | .any(|g| &g.name == name) { | |
173 | debugln!("Validator::validate_blacklist:iter: groups contains it..."); | |
174 | for n in self.0.arg_names_in_group(name) { | |
175 | debugln!("Validator::validate_blacklist:iter:iter: Checking arg '{}' in group...", | |
176 | n); | |
177 | if matcher.contains(n) { | |
178 | debugln!("Validator::validate_blacklist:iter:iter: matcher contains it..."); | |
179 | return Err(build_err!(self.0, &n, matcher)); | |
180 | } | |
181 | } | |
182 | } else if matcher.contains(name) { | |
183 | debugln!("Validator::validate_blacklist:iter: matcher contains it..."); | |
184 | return Err(build_err!(self.0, name, matcher)); | |
185 | } | |
186 | } | |
187 | Ok(()) | |
188 | } | |
189 | ||
190 | fn validate_matched_args(&self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> { | |
191 | debugln!("Validator::validate_matched_args;"); | |
192 | for (name, ma) in matcher.iter() { | |
193 | debugln!("Validator::validate_matched_args:iter:{}: vals={:#?}", | |
194 | name, | |
195 | ma.vals); | |
196 | if let Some(opt) = find_by_name!(self.0, name, opts, iter) { | |
197 | try!(self.validate_arg_num_vals(opt, ma, matcher)); | |
198 | try!(self.validate_values(opt, ma, matcher)); | |
199 | try!(self.validate_arg_requires(opt, ma, matcher)); | |
200 | try!(self.validate_arg_num_occurs(opt, ma, matcher)); | |
201 | } else if let Some(flag) = find_by_name!(self.0, name, flags, iter) { | |
202 | try!(self.validate_arg_requires(flag, ma, matcher)); | |
203 | try!(self.validate_arg_num_occurs(flag, ma, matcher)); | |
204 | } else if let Some(pos) = find_by_name!(self.0, name, positionals, values) { | |
205 | try!(self.validate_arg_num_vals(pos, ma, matcher)); | |
206 | try!(self.validate_arg_num_occurs(pos, ma, matcher)); | |
207 | try!(self.validate_values(pos, ma, matcher)); | |
208 | try!(self.validate_arg_requires(pos, ma, matcher)); | |
209 | } else { | |
210 | let grp = self.0 | |
211 | .groups | |
212 | .iter() | |
213 | .find(|g| &g.name == name) | |
214 | .expect(INTERNAL_ERROR_MSG); | |
215 | if let Some(ref g_reqs) = grp.requires { | |
216 | if g_reqs.iter().any(|&n| !matcher.contains(n)) { | |
217 | return self.missing_required_error(matcher, None); | |
218 | } | |
219 | } | |
220 | } | |
221 | } | |
222 | Ok(()) | |
223 | } | |
224 | ||
225 | fn validate_arg_num_occurs<A>(&self, | |
226 | a: &A, | |
227 | ma: &MatchedArg, | |
228 | matcher: &ArgMatcher) | |
229 | -> ClapResult<()> | |
230 | where A: AnyArg<'a, 'b> + Display | |
231 | { | |
232 | debugln!("Validator::validate_arg_num_occurs: a={};", a.name()); | |
233 | if ma.occurs > 1 && !a.is_set(ArgSettings::Multiple) { | |
234 | // Not the first time, and we don't allow multiples | |
235 | return Err(Error::unexpected_multiple_usage(a, | |
236 | &*usage::create_error_usage(self.0, | |
237 | matcher, | |
238 | None), | |
239 | self.0.color())); | |
240 | } | |
241 | Ok(()) | |
242 | } | |
243 | ||
244 | fn validate_arg_num_vals<A>(&self, | |
245 | a: &A, | |
246 | ma: &MatchedArg, | |
247 | matcher: &ArgMatcher) | |
248 | -> ClapResult<()> | |
249 | where A: AnyArg<'a, 'b> + Display | |
250 | { | |
251 | debugln!("Validator::validate_arg_num_vals;"); | |
252 | if let Some(num) = a.num_vals() { | |
253 | debugln!("Validator::validate_arg_num_vals: num_vals set...{}", num); | |
254 | let should_err = if a.is_set(ArgSettings::Multiple) { | |
255 | ((ma.vals.len() as u64) % num) != 0 | |
256 | } else { | |
257 | num != (ma.vals.len() as u64) | |
258 | }; | |
259 | if should_err { | |
260 | debugln!("Validator::validate_arg_num_vals: Sending error WrongNumberOfValues"); | |
261 | return Err(Error::wrong_number_of_values(a, | |
262 | num, | |
263 | if a.is_set(ArgSettings::Multiple) { | |
264 | (ma.vals.len() % num as usize) | |
265 | } else { | |
266 | ma.vals.len() | |
267 | }, | |
268 | if ma.vals.len() == 1 || | |
269 | (a.is_set(ArgSettings::Multiple) && | |
270 | (ma.vals.len() % num as usize) == | |
271 | 1) { | |
272 | "as" | |
273 | } else { | |
274 | "ere" | |
275 | }, | |
276 | &*usage::create_error_usage(self.0, | |
277 | matcher, | |
278 | None), | |
279 | self.0.color())); | |
280 | } | |
281 | } | |
282 | if let Some(num) = a.max_vals() { | |
283 | debugln!("Validator::validate_arg_num_vals: max_vals set...{}", num); | |
284 | if (ma.vals.len() as u64) > num { | |
285 | debugln!("Validator::validate_arg_num_vals: Sending error TooManyValues"); | |
286 | return Err(Error::too_many_values(ma.vals | |
287 | .iter() | |
288 | .last() | |
289 | .expect(INTERNAL_ERROR_MSG) | |
290 | .to_str() | |
291 | .expect(INVALID_UTF8), | |
292 | a, | |
293 | &*usage::create_error_usage(self.0, | |
294 | matcher, | |
295 | None), | |
296 | self.0.color())); | |
297 | } | |
298 | } | |
299 | if let Some(num) = a.min_vals() { | |
300 | debugln!("Validator::validate_arg_num_vals: min_vals set: {}", num); | |
301 | if (ma.vals.len() as u64) < num { | |
302 | debugln!("Validator::validate_arg_num_vals: Sending error TooFewValues"); | |
303 | return Err(Error::too_few_values(a, | |
304 | num, | |
305 | ma.vals.len(), | |
306 | &*usage::create_error_usage(self.0, | |
307 | matcher, | |
308 | None), | |
309 | self.0.color())); | |
310 | } | |
311 | } | |
312 | // Issue 665 (https://github.com/kbknapp/clap-rs/issues/665) | |
313 | if a.takes_value() && !a.is_set(ArgSettings::EmptyValues) && ma.vals.is_empty() { | |
314 | return Err(Error::empty_value(a, | |
315 | &*usage::create_error_usage(self.0, matcher, None), | |
316 | self.0.color())); | |
317 | } | |
318 | Ok(()) | |
319 | } | |
320 | ||
321 | fn validate_arg_requires<A>(&self, | |
322 | a: &A, | |
323 | ma: &MatchedArg, | |
324 | matcher: &ArgMatcher) | |
325 | -> ClapResult<()> | |
326 | where A: AnyArg<'a, 'b> + Display | |
327 | { | |
328 | debugln!("Validator::validate_arg_requires;"); | |
329 | if let Some(a_reqs) = a.requires() { | |
330 | for &(val, name) in a_reqs.iter().filter(|&&(val, _)| val.is_some()) { | |
331 | let missing_req = | |
332 | |v| v == val.expect(INTERNAL_ERROR_MSG) && !matcher.contains(name); | |
333 | if ma.vals.iter().any(missing_req) { | |
334 | return self.missing_required_error(matcher, None); | |
335 | } | |
336 | } | |
337 | } | |
338 | Ok(()) | |
339 | } | |
340 | ||
341 | fn validate_required(&self, matcher: &ArgMatcher) -> ClapResult<()> { | |
342 | debugln!("Validator::validate_required: required={:?};", | |
343 | self.0.required); | |
344 | 'outer: for name in &self.0.required { | |
345 | debugln!("Validator::validate_required:iter:{}:", name); | |
346 | if matcher.contains(name) { | |
347 | continue 'outer; | |
348 | } | |
349 | if let Some(a) = find_by_name!(self.0, name, flags, iter) { | |
350 | if self.is_missing_required_ok(a, matcher) { | |
351 | continue 'outer; | |
352 | } | |
353 | } else if let Some(a) = find_by_name!(self.0, name, opts, iter) { | |
354 | if self.is_missing_required_ok(a, matcher) { | |
355 | continue 'outer; | |
356 | } | |
357 | } else if let Some(a) = find_by_name!(self.0, name, positionals, values) { | |
358 | if self.is_missing_required_ok(a, matcher) { | |
359 | continue 'outer; | |
360 | } | |
361 | } | |
362 | return self.missing_required_error(matcher, None); | |
363 | } | |
364 | ||
365 | // Validate the conditionally required args | |
366 | for &(a, v, r) in &self.0.r_ifs { | |
367 | if let Some(ma) = matcher.get(a) { | |
368 | if matcher.get(r).is_none() && ma.vals.iter().any(|val| val == v) { | |
369 | return self.missing_required_error(matcher, Some(r)); | |
370 | } | |
371 | } | |
372 | } | |
373 | Ok(()) | |
374 | } | |
375 | ||
376 | fn validate_conflicts<A>(&self, a: &A, matcher: &ArgMatcher) -> Option<bool> | |
377 | where A: AnyArg<'a, 'b> | |
378 | { | |
379 | debugln!("Validator::validate_conflicts: a={:?};", a.name()); | |
380 | a.blacklist().map(|bl| { | |
381 | bl.iter().any(|conf| { | |
382 | matcher.contains(conf) || | |
383 | self.0 | |
384 | .groups | |
385 | .iter() | |
386 | .find(|g| &g.name == conf) | |
387 | .map_or(false, |g| g.args.iter().any(|arg| matcher.contains(arg))) | |
388 | }) | |
389 | }) | |
390 | } | |
391 | ||
392 | fn validate_required_unless<A>(&self, a: &A, matcher: &ArgMatcher) -> Option<bool> | |
393 | where A: AnyArg<'a, 'b> | |
394 | { | |
395 | debugln!("Validator::validate_required_unless: a={:?};", a.name()); | |
396 | macro_rules! check { | |
397 | ($how:ident, $_self:expr, $a:ident, $m:ident) => {{ | |
398 | $a.required_unless().map(|ru| { | |
399 | ru.iter().$how(|n| { | |
400 | $m.contains(n) || { | |
401 | if let Some(grp) = $_self.groups.iter().find(|g| &g.name == n) { | |
402 | grp.args.iter().any(|arg| $m.contains(arg)) | |
403 | } else { | |
404 | false | |
405 | } | |
406 | } | |
407 | }) | |
408 | }) | |
409 | }}; | |
410 | } | |
411 | if a.is_set(ArgSettings::RequiredUnlessAll) { | |
412 | check!(all, self.0, a, matcher) | |
413 | } else { | |
414 | check!(any, self.0, a, matcher) | |
415 | } | |
416 | } | |
417 | ||
418 | fn missing_required_error(&self, matcher: &ArgMatcher, extra: Option<&str>) -> ClapResult<()> { | |
419 | debugln!("Validator::missing_required_error: extra={:?}", extra); | |
420 | let c = Colorizer { | |
421 | use_stderr: true, | |
422 | when: self.0.color(), | |
423 | }; | |
424 | let mut reqs = self.0 | |
425 | .required | |
426 | .iter() | |
427 | .map(|&r| &*r) | |
428 | .collect::<Vec<_>>(); | |
429 | if let Some(r) = extra { | |
430 | reqs.push(r); | |
431 | } | |
432 | reqs.retain(|n| !matcher.contains(n)); | |
433 | reqs.dedup(); | |
434 | debugln!("Validator::missing_required_error: reqs={:#?}", reqs); | |
435 | let req_args = | |
436 | usage::get_required_usage_from(self.0, &reqs[..], Some(matcher), extra, true) | |
437 | .iter() | |
438 | .fold(String::new(), | |
439 | |acc, s| acc + &format!("\n {}", c.error(s))[..]); | |
440 | debugln!("Validator::missing_required_error: req_args={:#?}", req_args); | |
441 | Err(Error::missing_required_argument(&*req_args, | |
442 | &*usage::create_error_usage(self.0, matcher, extra), | |
443 | self.0.color())) | |
444 | } | |
445 | ||
446 | #[inline] | |
447 | fn is_missing_required_ok<A>(&self, a: &A, matcher: &ArgMatcher) -> bool | |
448 | where A: AnyArg<'a, 'b> | |
449 | { | |
450 | debugln!("Validator::is_missing_required_ok: a={}", a.name()); | |
451 | self.validate_conflicts(a, matcher).unwrap_or(false) || | |
452 | self.validate_required_unless(a, matcher).unwrap_or(false) | |
453 | } | |
454 | } |