1 | // Std |
2 | use std::{ |
3 | cell::Cell, |
4 | ffi::{OsStr, OsString}, |
5 | }; |
6 | |
7 | use clap_lex::OsStrExt as _; |
8 | |
9 | // Internal |
10 | use crate::builder::{Arg, Command}; |
11 | use crate::error::Error as ClapError; |
12 | use crate::error::Result as ClapResult; |
13 | use crate::mkeymap::KeyType; |
14 | use crate::output::Usage; |
15 | use crate::parser::features::suggestions; |
16 | use crate::parser::{ArgMatcher, SubCommand}; |
17 | use crate::parser::{Validator, ValueSource}; |
18 | use crate::util::AnyValue; |
19 | use crate::util::Id; |
20 | use crate::ArgAction; |
21 | use crate::INTERNAL_ERROR_MSG; |
22 | |
23 | pub(crate) struct Parser<'cmd> { |
24 | cmd: &'cmd mut Command, |
25 | cur_idx: Cell<usize>, |
26 | /// Index of the previous flag subcommand in a group of flags. |
27 | flag_subcmd_at: Option<usize>, |
28 | /// Counter indicating the number of items to skip |
29 | /// when revisiting the group of flags which includes the flag subcommand. |
30 | flag_subcmd_skip: usize, |
31 | } |
32 | |
33 | // Initializing Methods |
34 | impl<'cmd> Parser<'cmd> { |
35 | pub(crate) fn new(cmd: &'cmd mut Command) -> Self { |
36 | Parser { |
37 | cmd, |
38 | cur_idx: Cell::new(0), |
39 | flag_subcmd_at: None, |
40 | flag_subcmd_skip: 0, |
41 | } |
42 | } |
43 | } |
44 | |
45 | // Parsing Methods |
46 | impl<'cmd> Parser<'cmd> { |
47 | // The actual parsing function |
48 | #[allow (clippy::cognitive_complexity)] |
49 | pub(crate) fn get_matches_with( |
50 | &mut self, |
51 | matcher: &mut ArgMatcher, |
52 | raw_args: &mut clap_lex::RawArgs, |
53 | mut args_cursor: clap_lex::ArgCursor, |
54 | ) -> ClapResult<()> { |
55 | debug!("Parser::get_matches_with" ); |
56 | // Verify all positional assertions pass |
57 | |
58 | let mut subcmd_name: Option<String> = None; |
59 | let mut keep_state = false; |
60 | let mut parse_state = ParseState::ValuesDone; |
61 | let mut pos_counter = 1; |
62 | |
63 | // Already met any valid arg(then we shouldn't expect subcommands after it). |
64 | let mut valid_arg_found = false; |
65 | // If the user already passed '--'. Meaning only positional args follow. |
66 | let mut trailing_values = false; |
67 | |
68 | // Count of positional args |
69 | let positional_count = self |
70 | .cmd |
71 | .get_keymap() |
72 | .keys() |
73 | .filter(|x| x.is_position()) |
74 | .count(); |
75 | // If any arg sets .last(true) |
76 | let contains_last = self.cmd.get_arguments().any(|x| x.is_last_set()); |
77 | |
78 | while let Some(arg_os) = raw_args.next(&mut args_cursor) { |
79 | debug!( |
80 | "Parser::get_matches_with: Begin parsing '{:?}'" , |
81 | arg_os.to_value_os(), |
82 | ); |
83 | |
84 | // Has the user already passed '--'? Meaning only positional args follow |
85 | if !trailing_values { |
86 | if self.cmd.is_subcommand_precedence_over_arg_set() |
87 | || !matches!(parse_state, ParseState::Opt(_) | ParseState::Pos(_)) |
88 | { |
89 | // Does the arg match a subcommand name, or any of its aliases (if defined) |
90 | let sc_name = self.possible_subcommand(arg_os.to_value(), valid_arg_found); |
91 | debug!("Parser::get_matches_with: sc={sc_name:?}" ); |
92 | if let Some(sc_name) = sc_name { |
93 | if sc_name == "help" && !self.cmd.is_disable_help_subcommand_set() { |
94 | ok!(self.parse_help_subcommand(raw_args.remaining(&mut args_cursor))); |
95 | unreachable!("`parse_help_subcommand` always errors" ); |
96 | } else { |
97 | subcmd_name = Some(sc_name.to_owned()); |
98 | } |
99 | break; |
100 | } |
101 | } |
102 | |
103 | if arg_os.is_escape() { |
104 | if matches!(&parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) if |
105 | self.cmd[opt].is_allow_hyphen_values_set()) |
106 | { |
107 | // ParseResult::MaybeHyphenValue, do nothing |
108 | } else { |
109 | debug!("Parser::get_matches_with: setting TrailingVals=true" ); |
110 | trailing_values = true; |
111 | matcher.start_trailing(); |
112 | continue; |
113 | } |
114 | } else if let Some((long_arg, long_value)) = arg_os.to_long() { |
115 | let parse_result = ok!(self.parse_long_arg( |
116 | matcher, |
117 | long_arg, |
118 | long_value, |
119 | &parse_state, |
120 | pos_counter, |
121 | &mut valid_arg_found, |
122 | )); |
123 | debug!("Parser::get_matches_with: After parse_long_arg {parse_result:?}" ); |
124 | match parse_result { |
125 | ParseResult::NoArg => { |
126 | unreachable!("`to_long` always has the flag specified" ) |
127 | } |
128 | ParseResult::ValuesDone => { |
129 | parse_state = ParseState::ValuesDone; |
130 | continue; |
131 | } |
132 | ParseResult::Opt(id) => { |
133 | parse_state = ParseState::Opt(id); |
134 | continue; |
135 | } |
136 | ParseResult::FlagSubCommand(name) => { |
137 | debug!( |
138 | "Parser::get_matches_with: FlagSubCommand found in long arg {:?}" , |
139 | &name |
140 | ); |
141 | subcmd_name = Some(name); |
142 | break; |
143 | } |
144 | ParseResult::EqualsNotProvided { arg } => { |
145 | let _ = self.resolve_pending(matcher); |
146 | return Err(ClapError::no_equals( |
147 | self.cmd, |
148 | arg, |
149 | Usage::new(self.cmd).create_usage_with_title(&[]), |
150 | )); |
151 | } |
152 | ParseResult::NoMatchingArg { arg } => { |
153 | let _ = self.resolve_pending(matcher); |
154 | let remaining_args: Vec<_> = |
155 | raw_args.remaining(&mut args_cursor).collect(); |
156 | return Err(self.did_you_mean_error( |
157 | &arg, |
158 | matcher, |
159 | &remaining_args, |
160 | trailing_values, |
161 | )); |
162 | } |
163 | ParseResult::UnneededAttachedValue { rest, used, arg } => { |
164 | let _ = self.resolve_pending(matcher); |
165 | return Err(ClapError::too_many_values( |
166 | self.cmd, |
167 | rest, |
168 | arg, |
169 | Usage::new(self.cmd).create_usage_with_title(&used), |
170 | )); |
171 | } |
172 | ParseResult::MaybeHyphenValue => { |
173 | // Maybe a hyphen value, do nothing. |
174 | } |
175 | ParseResult::AttachedValueNotConsumed => { |
176 | unreachable!() |
177 | } |
178 | } |
179 | } else if let Some(short_arg) = arg_os.to_short() { |
180 | // Arg looks like a short flag, and not a possible number |
181 | |
182 | // Try to parse short args like normal, if allow_hyphen_values or |
183 | // AllowNegativeNumbers is set, parse_short_arg will *not* throw |
184 | // an error, and instead return Ok(None) |
185 | let parse_result = ok!(self.parse_short_arg( |
186 | matcher, |
187 | short_arg, |
188 | &parse_state, |
189 | pos_counter, |
190 | &mut valid_arg_found, |
191 | )); |
192 | // If it's None, we then check if one of those two AppSettings was set |
193 | debug!("Parser::get_matches_with: After parse_short_arg {parse_result:?}" ); |
194 | match parse_result { |
195 | ParseResult::NoArg => { |
196 | // Is a single dash `-`, try positional. |
197 | } |
198 | ParseResult::ValuesDone => { |
199 | parse_state = ParseState::ValuesDone; |
200 | continue; |
201 | } |
202 | ParseResult::Opt(id) => { |
203 | parse_state = ParseState::Opt(id); |
204 | continue; |
205 | } |
206 | ParseResult::FlagSubCommand(name) => { |
207 | // If there are more short flags to be processed, we should keep the state, and later |
208 | // revisit the current group of short flags skipping the subcommand. |
209 | keep_state = self |
210 | .flag_subcmd_at |
211 | .map(|at| { |
212 | raw_args |
213 | .seek(&mut args_cursor, clap_lex::SeekFrom::Current(-1)); |
214 | // Since we are now saving the current state, the number of flags to skip during state recovery should |
215 | // be the current index (`cur_idx`) minus ONE UNIT TO THE LEFT of the starting position. |
216 | self.flag_subcmd_skip = self.cur_idx.get() - at + 1; |
217 | }) |
218 | .is_some(); |
219 | |
220 | debug!( |
221 | "Parser::get_matches_with:FlagSubCommandShort: subcmd_name={}, keep_state={}, flag_subcmd_skip={}" , |
222 | name, |
223 | keep_state, |
224 | self.flag_subcmd_skip |
225 | ); |
226 | |
227 | subcmd_name = Some(name); |
228 | break; |
229 | } |
230 | ParseResult::EqualsNotProvided { arg } => { |
231 | let _ = self.resolve_pending(matcher); |
232 | return Err(ClapError::no_equals( |
233 | self.cmd, |
234 | arg, |
235 | Usage::new(self.cmd).create_usage_with_title(&[]), |
236 | )); |
237 | } |
238 | ParseResult::NoMatchingArg { arg } => { |
239 | let _ = self.resolve_pending(matcher); |
240 | // We already know it looks like a flag |
241 | let suggested_trailing_arg = |
242 | !trailing_values && self.cmd.has_positionals(); |
243 | return Err(ClapError::unknown_argument( |
244 | self.cmd, |
245 | arg, |
246 | None, |
247 | suggested_trailing_arg, |
248 | Usage::new(self.cmd).create_usage_with_title(&[]), |
249 | )); |
250 | } |
251 | ParseResult::MaybeHyphenValue => { |
252 | // Maybe a hyphen value, do nothing. |
253 | } |
254 | ParseResult::UnneededAttachedValue { .. } |
255 | | ParseResult::AttachedValueNotConsumed => unreachable!(), |
256 | } |
257 | } |
258 | |
259 | if let ParseState::Opt(id) = &parse_state { |
260 | // Assume this is a value of a previous arg. |
261 | |
262 | // get the option so we can check the settings |
263 | let arg = &self.cmd[id]; |
264 | let parse_result = if let Some(parse_result) = |
265 | self.check_terminator(arg, arg_os.to_value_os()) |
266 | { |
267 | parse_result |
268 | } else { |
269 | let trailing_values = false; |
270 | let arg_values = matcher.pending_values_mut(id, None, trailing_values); |
271 | arg_values.push(arg_os.to_value_os().to_owned()); |
272 | if matcher.needs_more_vals(arg) { |
273 | ParseResult::Opt(arg.get_id().clone()) |
274 | } else { |
275 | ParseResult::ValuesDone |
276 | } |
277 | }; |
278 | parse_state = match parse_result { |
279 | ParseResult::Opt(id) => ParseState::Opt(id), |
280 | ParseResult::ValuesDone => ParseState::ValuesDone, |
281 | _ => unreachable!(), |
282 | }; |
283 | // get the next value from the iterator |
284 | continue; |
285 | } |
286 | } |
287 | |
288 | // Correct pos_counter. |
289 | pos_counter = { |
290 | let is_second_to_last = pos_counter + 1 == positional_count; |
291 | |
292 | // The last positional argument, or second to last positional |
293 | // argument may be set to .multiple_values(true) or `.multiple_occurrences(true)` |
294 | let low_index_mults = is_second_to_last |
295 | && self.cmd.get_positionals().any(|a| { |
296 | a.is_multiple() && (positional_count != a.get_index().unwrap_or(0)) |
297 | }) |
298 | && self |
299 | .cmd |
300 | .get_positionals() |
301 | .last() |
302 | .map(|p_name| !p_name.is_last_set()) |
303 | .unwrap_or_default(); |
304 | |
305 | let is_terminated = self |
306 | .cmd |
307 | .get_keymap() |
308 | .get(&pos_counter) |
309 | .map(|a| a.get_value_terminator().is_some()) |
310 | .unwrap_or_default(); |
311 | |
312 | let missing_pos = self.cmd.is_allow_missing_positional_set() |
313 | && is_second_to_last |
314 | && !trailing_values; |
315 | |
316 | debug!("Parser::get_matches_with: Positional counter...{pos_counter}" ); |
317 | debug!("Parser::get_matches_with: Low index multiples...{low_index_mults:?}" ); |
318 | |
319 | if (low_index_mults || missing_pos) && !is_terminated { |
320 | let skip_current = if let Some(n) = raw_args.peek(&args_cursor) { |
321 | if let Some(arg) = self |
322 | .cmd |
323 | .get_positionals() |
324 | .find(|a| a.get_index() == Some(pos_counter)) |
325 | { |
326 | // If next value looks like a new_arg or it's a |
327 | // subcommand, skip positional argument under current |
328 | // pos_counter(which means current value cannot be a |
329 | // positional argument with a value next to it), assume |
330 | // current value matches the next arg. |
331 | self.is_new_arg(&n, arg) |
332 | || self |
333 | .possible_subcommand(n.to_value(), valid_arg_found) |
334 | .is_some() |
335 | } else { |
336 | true |
337 | } |
338 | } else { |
339 | true |
340 | }; |
341 | |
342 | if skip_current { |
343 | debug!("Parser::get_matches_with: Bumping the positional counter..." ); |
344 | pos_counter + 1 |
345 | } else { |
346 | pos_counter |
347 | } |
348 | } else if trailing_values |
349 | && (self.cmd.is_allow_missing_positional_set() || contains_last) |
350 | { |
351 | // Came to -- and one positional has .last(true) set, so we go immediately |
352 | // to the last (highest index) positional |
353 | debug!("Parser::get_matches_with: .last(true) and --, setting last pos" ); |
354 | positional_count |
355 | } else { |
356 | pos_counter |
357 | } |
358 | }; |
359 | |
360 | if let Some(arg) = self.cmd.get_keymap().get(&pos_counter) { |
361 | if arg.is_last_set() && !trailing_values { |
362 | let _ = self.resolve_pending(matcher); |
363 | // Its already considered a positional, we don't need to suggest turning it |
364 | // into one |
365 | let suggested_trailing_arg = false; |
366 | return Err(ClapError::unknown_argument( |
367 | self.cmd, |
368 | arg_os.display().to_string(), |
369 | None, |
370 | suggested_trailing_arg, |
371 | Usage::new(self.cmd).create_usage_with_title(&[]), |
372 | )); |
373 | } |
374 | |
375 | if arg.is_trailing_var_arg_set() { |
376 | trailing_values = true; |
377 | } |
378 | |
379 | if matcher.pending_arg_id() != Some(arg.get_id()) || !arg.is_multiple_values_set() { |
380 | ok!(self.resolve_pending(matcher)); |
381 | } |
382 | parse_state = |
383 | if let Some(parse_result) = self.check_terminator(arg, arg_os.to_value_os()) { |
384 | debug_assert_eq!(parse_result, ParseResult::ValuesDone); |
385 | pos_counter += 1; |
386 | ParseState::ValuesDone |
387 | } else { |
388 | let arg_values = matcher.pending_values_mut( |
389 | arg.get_id(), |
390 | Some(Identifier::Index), |
391 | trailing_values, |
392 | ); |
393 | arg_values.push(arg_os.to_value_os().to_owned()); |
394 | |
395 | // Only increment the positional counter if it doesn't allow multiples |
396 | if !arg.is_multiple() { |
397 | pos_counter += 1; |
398 | ParseState::ValuesDone |
399 | } else { |
400 | ParseState::Pos(arg.get_id().clone()) |
401 | } |
402 | }; |
403 | valid_arg_found = true; |
404 | } else if let Some(external_parser) = |
405 | self.cmd.get_external_subcommand_value_parser().cloned() |
406 | { |
407 | // Get external subcommand name |
408 | let sc_name = match arg_os.to_value() { |
409 | Ok(s) => s.to_owned(), |
410 | Err(_) => { |
411 | let _ = self.resolve_pending(matcher); |
412 | return Err(ClapError::invalid_utf8( |
413 | self.cmd, |
414 | Usage::new(self.cmd).create_usage_with_title(&[]), |
415 | )); |
416 | } |
417 | }; |
418 | |
419 | // Collect the external subcommand args |
420 | let mut sc_m = ArgMatcher::new(self.cmd); |
421 | sc_m.start_occurrence_of_external(self.cmd); |
422 | |
423 | for raw_val in raw_args.remaining(&mut args_cursor) { |
424 | let val = ok!(external_parser.parse_ref( |
425 | self.cmd, |
426 | None, |
427 | raw_val, |
428 | ValueSource::CommandLine |
429 | )); |
430 | let external_id = Id::from_static_ref(Id::EXTERNAL); |
431 | sc_m.add_val_to(&external_id, val, raw_val.to_os_string()); |
432 | } |
433 | |
434 | matcher.subcommand(SubCommand { |
435 | name: sc_name, |
436 | matches: sc_m.into_inner(), |
437 | }); |
438 | |
439 | ok!(self.resolve_pending(matcher)); |
440 | #[cfg (feature = "env" )] |
441 | ok!(self.add_env(matcher)); |
442 | ok!(self.add_defaults(matcher)); |
443 | return Validator::new(self.cmd).validate(parse_state, matcher); |
444 | } else { |
445 | // Start error processing |
446 | let _ = self.resolve_pending(matcher); |
447 | return Err(self.match_arg_error(&arg_os, valid_arg_found, trailing_values)); |
448 | } |
449 | } |
450 | |
451 | if let Some(ref pos_sc_name) = subcmd_name { |
452 | let sc_name = self |
453 | .cmd |
454 | .find_subcommand(pos_sc_name) |
455 | .expect(INTERNAL_ERROR_MSG) |
456 | .get_name() |
457 | .to_owned(); |
458 | ok!(self.parse_subcommand(&sc_name, matcher, raw_args, args_cursor, keep_state)); |
459 | } |
460 | |
461 | ok!(self.resolve_pending(matcher)); |
462 | #[cfg (feature = "env" )] |
463 | ok!(self.add_env(matcher)); |
464 | ok!(self.add_defaults(matcher)); |
465 | Validator::new(self.cmd).validate(parse_state, matcher) |
466 | } |
467 | |
468 | fn match_arg_error( |
469 | &self, |
470 | arg_os: &clap_lex::ParsedArg<'_>, |
471 | valid_arg_found: bool, |
472 | trailing_values: bool, |
473 | ) -> ClapError { |
474 | // If argument follows a `--` |
475 | if trailing_values { |
476 | // If the arg matches a subcommand name, or any of its aliases (if defined) |
477 | if self |
478 | .possible_subcommand(arg_os.to_value(), valid_arg_found) |
479 | .is_some() |
480 | { |
481 | return ClapError::unnecessary_double_dash( |
482 | self.cmd, |
483 | arg_os.display().to_string(), |
484 | Usage::new(self.cmd).create_usage_with_title(&[]), |
485 | ); |
486 | } |
487 | } |
488 | |
489 | let suggested_trailing_arg = !trailing_values |
490 | && self.cmd.has_positionals() |
491 | && (arg_os.is_long() || arg_os.is_short()); |
492 | |
493 | if !(self.cmd.is_args_conflicts_with_subcommands_set() && valid_arg_found) { |
494 | let candidates = suggestions::did_you_mean( |
495 | &arg_os.display().to_string(), |
496 | self.cmd.all_subcommand_names(), |
497 | ); |
498 | // If the argument looks like a subcommand. |
499 | if !candidates.is_empty() { |
500 | return ClapError::invalid_subcommand( |
501 | self.cmd, |
502 | arg_os.display().to_string(), |
503 | candidates, |
504 | self.cmd |
505 | .get_bin_name() |
506 | .unwrap_or_else(|| self.cmd.get_name()) |
507 | .to_owned(), |
508 | suggested_trailing_arg, |
509 | Usage::new(self.cmd).create_usage_with_title(&[]), |
510 | ); |
511 | } |
512 | |
513 | // If the argument must be a subcommand. |
514 | if self.cmd.has_subcommands() |
515 | && (!self.cmd.has_positionals() || self.cmd.is_infer_subcommands_set()) |
516 | { |
517 | return ClapError::unrecognized_subcommand( |
518 | self.cmd, |
519 | arg_os.display().to_string(), |
520 | Usage::new(self.cmd).create_usage_with_title(&[]), |
521 | ); |
522 | } |
523 | } |
524 | |
525 | ClapError::unknown_argument( |
526 | self.cmd, |
527 | arg_os.display().to_string(), |
528 | None, |
529 | suggested_trailing_arg, |
530 | Usage::new(self.cmd).create_usage_with_title(&[]), |
531 | ) |
532 | } |
533 | |
534 | // Checks if the arg matches a subcommand name, or any of its aliases (if defined) |
535 | fn possible_subcommand( |
536 | &self, |
537 | arg: Result<&str, &OsStr>, |
538 | valid_arg_found: bool, |
539 | ) -> Option<&str> { |
540 | debug!("Parser::possible_subcommand: arg={arg:?}" ); |
541 | let arg = some!(arg.ok()); |
542 | |
543 | if !(self.cmd.is_args_conflicts_with_subcommands_set() && valid_arg_found) { |
544 | if self.cmd.is_infer_subcommands_set() { |
545 | // For subcommand `test`, we accepts it's prefix: `t`, `te`, |
546 | // `tes` and `test`. |
547 | let v = self |
548 | .cmd |
549 | .all_subcommand_names() |
550 | .filter(|s| s.starts_with(arg)) |
551 | .collect::<Vec<_>>(); |
552 | |
553 | if v.len() == 1 { |
554 | return Some(v[0]); |
555 | } |
556 | |
557 | // If there is any ambiguity, fallback to non-infer subcommand |
558 | // search. |
559 | } |
560 | if let Some(sc) = self.cmd.find_subcommand(arg) { |
561 | return Some(sc.get_name()); |
562 | } |
563 | } |
564 | None |
565 | } |
566 | |
567 | // Checks if the arg matches a long flag subcommand name, or any of its aliases (if defined) |
568 | fn possible_long_flag_subcommand(&self, arg: &str) -> Option<&str> { |
569 | debug!("Parser::possible_long_flag_subcommand: arg={arg:?}" ); |
570 | if self.cmd.is_infer_subcommands_set() { |
571 | let options = self |
572 | .cmd |
573 | .get_subcommands() |
574 | .fold(Vec::new(), |mut options, sc| { |
575 | if let Some(long) = sc.get_long_flag() { |
576 | if long.starts_with(arg) { |
577 | options.push(long); |
578 | } |
579 | options.extend(sc.get_all_aliases().filter(|alias| alias.starts_with(arg))) |
580 | } |
581 | options |
582 | }); |
583 | if options.len() == 1 { |
584 | return Some(options[0]); |
585 | } |
586 | |
587 | for sc in options { |
588 | if sc == arg { |
589 | return Some(sc); |
590 | } |
591 | } |
592 | } else if let Some(sc_name) = self.cmd.find_long_subcmd(arg) { |
593 | return Some(sc_name); |
594 | } |
595 | None |
596 | } |
597 | |
598 | fn parse_help_subcommand( |
599 | &self, |
600 | cmds: impl Iterator<Item = &'cmd OsStr>, |
601 | ) -> ClapResult<std::convert::Infallible> { |
602 | debug!("Parser::parse_help_subcommand" ); |
603 | |
604 | let mut cmd = self.cmd.clone(); |
605 | let sc = { |
606 | let mut sc = &mut cmd; |
607 | |
608 | for cmd in cmds { |
609 | sc = if let Some(sc_name) = |
610 | sc.find_subcommand(cmd).map(|sc| sc.get_name().to_owned()) |
611 | { |
612 | sc._build_subcommand(&sc_name).unwrap() |
613 | } else { |
614 | return Err(ClapError::unrecognized_subcommand( |
615 | sc, |
616 | cmd.to_string_lossy().into_owned(), |
617 | Usage::new(sc).create_usage_with_title(&[]), |
618 | )); |
619 | }; |
620 | } |
621 | |
622 | sc |
623 | }; |
624 | let parser = Parser::new(sc); |
625 | |
626 | Err(parser.help_err(true)) |
627 | } |
628 | |
629 | fn is_new_arg(&self, next: &clap_lex::ParsedArg<'_>, current_positional: &Arg) -> bool { |
630 | #![allow (clippy::needless_bool)] // Prefer consistent if/else-if ladder |
631 | |
632 | debug!( |
633 | "Parser::is_new_arg: {:?}:{}" , |
634 | next.to_value_os(), |
635 | current_positional.get_id() |
636 | ); |
637 | |
638 | if self.cmd[current_positional.get_id()].is_allow_hyphen_values_set() |
639 | || (self.cmd[current_positional.get_id()].is_allow_negative_numbers_set() |
640 | && next.is_number()) |
641 | { |
642 | // If allow hyphen, this isn't a new arg. |
643 | debug!("Parser::is_new_arg: Allow hyphen" ); |
644 | false |
645 | } else if next.is_long() { |
646 | // If this is a long flag, this is a new arg. |
647 | debug!("Parser::is_new_arg: --<something> found" ); |
648 | true |
649 | } else if next.is_short() { |
650 | // If this is a short flag, this is a new arg. But a singe '-' by |
651 | // itself is a value and typically means "stdin" on unix systems. |
652 | debug!("Parser::is_new_arg: -<something> found" ); |
653 | true |
654 | } else { |
655 | // Nothing special, this is a value. |
656 | debug!("Parser::is_new_arg: value" ); |
657 | false |
658 | } |
659 | } |
660 | |
661 | fn parse_subcommand( |
662 | &mut self, |
663 | sc_name: &str, |
664 | matcher: &mut ArgMatcher, |
665 | raw_args: &mut clap_lex::RawArgs, |
666 | args_cursor: clap_lex::ArgCursor, |
667 | keep_state: bool, |
668 | ) -> ClapResult<()> { |
669 | debug!("Parser::parse_subcommand" ); |
670 | |
671 | let partial_parsing_enabled = self.cmd.is_ignore_errors_set(); |
672 | |
673 | if let Some(sc) = self.cmd._build_subcommand(sc_name) { |
674 | let mut sc_matcher = ArgMatcher::new(sc); |
675 | |
676 | debug!( |
677 | "Parser::parse_subcommand: About to parse sc={}" , |
678 | sc.get_name() |
679 | ); |
680 | |
681 | { |
682 | let mut p = Parser::new(sc); |
683 | // HACK: maintain indexes between parsers |
684 | // FlagSubCommand short arg needs to revisit the current short args, but skip the subcommand itself |
685 | if keep_state { |
686 | p.cur_idx.set(self.cur_idx.get()); |
687 | p.flag_subcmd_at = self.flag_subcmd_at; |
688 | p.flag_subcmd_skip = self.flag_subcmd_skip; |
689 | } |
690 | if let Err(error) = p.get_matches_with(&mut sc_matcher, raw_args, args_cursor) { |
691 | if partial_parsing_enabled { |
692 | debug!("Parser::parse_subcommand: ignored error in subcommand {sc_name}: {error:?}" ); |
693 | } else { |
694 | return Err(error); |
695 | } |
696 | } |
697 | } |
698 | matcher.subcommand(SubCommand { |
699 | name: sc.get_name().to_owned(), |
700 | matches: sc_matcher.into_inner(), |
701 | }); |
702 | } |
703 | Ok(()) |
704 | } |
705 | |
706 | fn parse_long_arg( |
707 | &mut self, |
708 | matcher: &mut ArgMatcher, |
709 | long_arg: Result<&str, &OsStr>, |
710 | long_value: Option<&OsStr>, |
711 | parse_state: &ParseState, |
712 | pos_counter: usize, |
713 | valid_arg_found: &mut bool, |
714 | ) -> ClapResult<ParseResult> { |
715 | // maybe here lifetime should be 'a |
716 | debug!("Parser::parse_long_arg" ); |
717 | |
718 | #[allow (clippy::blocks_in_if_conditions)] |
719 | if matches!(parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) if |
720 | self.cmd[opt].is_allow_hyphen_values_set()) |
721 | { |
722 | debug!("Parser::parse_long_arg: prior arg accepts hyphenated values" ,); |
723 | return Ok(ParseResult::MaybeHyphenValue); |
724 | } |
725 | |
726 | debug!("Parser::parse_long_arg: Does it contain '='..." ); |
727 | let long_arg = match long_arg { |
728 | Ok(long_arg) => long_arg, |
729 | Err(long_arg_os) => { |
730 | return Ok(ParseResult::NoMatchingArg { |
731 | arg: long_arg_os.to_string_lossy().into_owned(), |
732 | }) |
733 | } |
734 | }; |
735 | if long_arg.is_empty() { |
736 | debug_assert!( |
737 | long_value.is_some(), |
738 | "`--` should be filtered out before this point" |
739 | ); |
740 | } |
741 | |
742 | let arg = if let Some(arg) = self.cmd.get_keymap().get(long_arg) { |
743 | debug!("Parser::parse_long_arg: Found valid arg or flag '{arg}'" ); |
744 | Some((long_arg, arg)) |
745 | } else if self.cmd.is_infer_long_args_set() { |
746 | let mut iter = self.cmd.get_arguments().filter_map(|a| { |
747 | if let Some(long) = a.get_long() { |
748 | if long.starts_with(long_arg) { |
749 | return Some((long, a)); |
750 | } |
751 | } |
752 | a.aliases |
753 | .iter() |
754 | .find_map(|(alias, _)| alias.starts_with(long_arg).then(|| (alias.as_str(), a))) |
755 | }); |
756 | |
757 | iter.next().filter(|_| iter.next().is_none()) |
758 | } else { |
759 | None |
760 | }; |
761 | |
762 | if let Some((_long_arg, arg)) = arg { |
763 | let ident = Identifier::Long; |
764 | *valid_arg_found = true; |
765 | if arg.is_takes_value_set() { |
766 | debug!( |
767 | "Parser::parse_long_arg({:?}): Found an arg with value '{:?}'" , |
768 | long_arg, &long_value |
769 | ); |
770 | let has_eq = long_value.is_some(); |
771 | self.parse_opt_value(ident, long_value, arg, matcher, has_eq) |
772 | } else if let Some(rest) = long_value { |
773 | let required = self.cmd.required_graph(); |
774 | debug!("Parser::parse_long_arg({long_arg:?}): Got invalid literal `{rest:?}`" ); |
775 | let mut used: Vec<Id> = matcher |
776 | .arg_ids() |
777 | .filter(|arg_id| { |
778 | matcher.check_explicit(arg_id, &crate::builder::ArgPredicate::IsPresent) |
779 | }) |
780 | .filter(|&n| { |
781 | self.cmd |
782 | .find(n) |
783 | .map(|a| !(a.is_hide_set() || required.contains(a.get_id()))) |
784 | .unwrap_or(true) |
785 | }) |
786 | .cloned() |
787 | .collect(); |
788 | used.push(arg.get_id().clone()); |
789 | |
790 | Ok(ParseResult::UnneededAttachedValue { |
791 | rest: rest.to_string_lossy().into_owned(), |
792 | used, |
793 | arg: arg.to_string(), |
794 | }) |
795 | } else { |
796 | debug!("Parser::parse_long_arg({long_arg:?}): Presence validated" ); |
797 | let trailing_idx = None; |
798 | self.react( |
799 | Some(ident), |
800 | ValueSource::CommandLine, |
801 | arg, |
802 | vec![], |
803 | trailing_idx, |
804 | matcher, |
805 | ) |
806 | } |
807 | } else if let Some(sc_name) = self.possible_long_flag_subcommand(long_arg) { |
808 | Ok(ParseResult::FlagSubCommand(sc_name.to_string())) |
809 | } else if self |
810 | .cmd |
811 | .get_keymap() |
812 | .get(&pos_counter) |
813 | .map(|arg| arg.is_allow_hyphen_values_set() && !arg.is_last_set()) |
814 | .unwrap_or_default() |
815 | { |
816 | debug!("Parser::parse_long_args: positional at {pos_counter} allows hyphens" ); |
817 | Ok(ParseResult::MaybeHyphenValue) |
818 | } else { |
819 | Ok(ParseResult::NoMatchingArg { |
820 | arg: long_arg.to_owned(), |
821 | }) |
822 | } |
823 | } |
824 | |
825 | fn parse_short_arg( |
826 | &mut self, |
827 | matcher: &mut ArgMatcher, |
828 | mut short_arg: clap_lex::ShortFlags<'_>, |
829 | parse_state: &ParseState, |
830 | // change this to possible pos_arg when removing the usage of &mut Parser. |
831 | pos_counter: usize, |
832 | valid_arg_found: &mut bool, |
833 | ) -> ClapResult<ParseResult> { |
834 | debug!("Parser::parse_short_arg: short_arg={short_arg:?}" ); |
835 | |
836 | #[allow (clippy::blocks_in_if_conditions)] |
837 | if matches!(parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) |
838 | if self.cmd[opt].is_allow_hyphen_values_set() || (self.cmd[opt].is_allow_negative_numbers_set() && short_arg.is_number())) |
839 | { |
840 | debug!("Parser::parse_short_args: prior arg accepts hyphenated values" ,); |
841 | return Ok(ParseResult::MaybeHyphenValue); |
842 | } else if self |
843 | .cmd |
844 | .get_keymap() |
845 | .get(&pos_counter) |
846 | .map(|arg| arg.is_allow_negative_numbers_set()) |
847 | .unwrap_or_default() |
848 | && short_arg.is_number() |
849 | { |
850 | debug!("Parser::parse_short_arg: negative number" ); |
851 | return Ok(ParseResult::MaybeHyphenValue); |
852 | } else if self |
853 | .cmd |
854 | .get_keymap() |
855 | .get(&pos_counter) |
856 | .map(|arg| arg.is_allow_hyphen_values_set() && !arg.is_last_set()) |
857 | .unwrap_or_default() |
858 | && short_arg |
859 | .clone() |
860 | .any(|c| !c.map(|c| self.cmd.contains_short(c)).unwrap_or_default()) |
861 | { |
862 | debug!("Parser::parse_short_args: positional at {pos_counter} allows hyphens" ); |
863 | return Ok(ParseResult::MaybeHyphenValue); |
864 | } |
865 | |
866 | let mut ret = ParseResult::NoArg; |
867 | |
868 | let skip = self.flag_subcmd_skip; |
869 | self.flag_subcmd_skip = 0; |
870 | let res = short_arg.advance_by(skip); |
871 | debug_assert_eq!( |
872 | res, |
873 | Ok(()), |
874 | "tracking of `flag_subcmd_skip` is off for ` {short_arg:?}`" |
875 | ); |
876 | while let Some(c) = short_arg.next_flag() { |
877 | let c = match c { |
878 | Ok(c) => c, |
879 | Err(rest) => { |
880 | return Ok(ParseResult::NoMatchingArg { |
881 | arg: format!("- {}" , rest.to_string_lossy()), |
882 | }); |
883 | } |
884 | }; |
885 | debug!("Parser::parse_short_arg:iter:{c}" ); |
886 | |
887 | // Check for matching short options, and return the name if there is no trailing |
888 | // concatenated value: -oval |
889 | // Option: -o |
890 | // Value: val |
891 | if let Some(arg) = self.cmd.get_keymap().get(&c) { |
892 | let ident = Identifier::Short; |
893 | debug!("Parser::parse_short_arg:iter:{c}: Found valid opt or flag" ); |
894 | *valid_arg_found = true; |
895 | if !arg.is_takes_value_set() { |
896 | let arg_values = Vec::new(); |
897 | let trailing_idx = None; |
898 | ret = ok!(self.react( |
899 | Some(ident), |
900 | ValueSource::CommandLine, |
901 | arg, |
902 | arg_values, |
903 | trailing_idx, |
904 | matcher, |
905 | )); |
906 | continue; |
907 | } |
908 | |
909 | // Check for trailing concatenated value |
910 | // |
911 | // Cloning the iterator, so we rollback if it isn't there. |
912 | let val = short_arg.clone().next_value_os().unwrap_or_default(); |
913 | debug!("Parser::parse_short_arg:iter:{c}: val={val:?}, short_arg={short_arg:?}" ); |
914 | let val = Some(val).filter(|v| !v.is_empty()); |
915 | |
916 | // Default to "we're expecting a value later". |
917 | // |
918 | // If attached value is not consumed, we may have more short |
919 | // flags to parse, continue. |
920 | // |
921 | // e.g. `-xvf`, when require_equals && x.min_vals == 0, we don't |
922 | // consume the `vf`, even if it's provided as value. |
923 | let (val, has_eq) = if let Some(val) = val.and_then(|v| v.strip_prefix("=" )) { |
924 | (Some(val), true) |
925 | } else { |
926 | (val, false) |
927 | }; |
928 | match ok!(self.parse_opt_value(ident, val, arg, matcher, has_eq)) { |
929 | ParseResult::AttachedValueNotConsumed => continue, |
930 | x => return Ok(x), |
931 | } |
932 | } |
933 | |
934 | return if let Some(sc_name) = self.cmd.find_short_subcmd(c) { |
935 | debug!("Parser::parse_short_arg:iter:{c}: subcommand={sc_name}" ); |
936 | // Make sure indices get updated before reading `self.cur_idx` |
937 | ok!(self.resolve_pending(matcher)); |
938 | self.cur_idx.set(self.cur_idx.get() + 1); |
939 | debug!("Parser::parse_short_arg: cur_idx:={}" , self.cur_idx.get()); |
940 | |
941 | let name = sc_name.to_string(); |
942 | // Get the index of the previously saved flag subcommand in the group of flags (if exists). |
943 | // If it is a new flag subcommand, then the formentioned index should be the current one |
944 | // (ie. `cur_idx`), and should be registered. |
945 | let cur_idx = self.cur_idx.get(); |
946 | self.flag_subcmd_at.get_or_insert(cur_idx); |
947 | let done_short_args = short_arg.is_empty(); |
948 | if done_short_args { |
949 | self.flag_subcmd_at = None; |
950 | } |
951 | Ok(ParseResult::FlagSubCommand(name)) |
952 | } else { |
953 | Ok(ParseResult::NoMatchingArg { |
954 | arg: format!("- {c}" ), |
955 | }) |
956 | }; |
957 | } |
958 | Ok(ret) |
959 | } |
960 | |
961 | fn parse_opt_value( |
962 | &self, |
963 | ident: Identifier, |
964 | attached_value: Option<&OsStr>, |
965 | arg: &Arg, |
966 | matcher: &mut ArgMatcher, |
967 | has_eq: bool, |
968 | ) -> ClapResult<ParseResult> { |
969 | debug!( |
970 | "Parser::parse_opt_value; arg={}, val={:?}, has_eq={:?}" , |
971 | arg.get_id(), |
972 | attached_value, |
973 | has_eq |
974 | ); |
975 | debug!("Parser::parse_opt_value; arg.settings={:?}" , arg.settings); |
976 | |
977 | debug!("Parser::parse_opt_value; Checking for val..." ); |
978 | // require_equals is set, but no '=' is provided, try throwing error. |
979 | if arg.is_require_equals_set() && !has_eq { |
980 | if arg.get_min_vals() == 0 { |
981 | debug!("Requires equals, but min_vals == 0" ); |
982 | let arg_values = Vec::new(); |
983 | let trailing_idx = None; |
984 | let react_result = ok!(self.react( |
985 | Some(ident), |
986 | ValueSource::CommandLine, |
987 | arg, |
988 | arg_values, |
989 | trailing_idx, |
990 | matcher, |
991 | )); |
992 | debug_assert_eq!(react_result, ParseResult::ValuesDone); |
993 | if attached_value.is_some() { |
994 | Ok(ParseResult::AttachedValueNotConsumed) |
995 | } else { |
996 | Ok(ParseResult::ValuesDone) |
997 | } |
998 | } else { |
999 | debug!("Requires equals but not provided. Error." ); |
1000 | Ok(ParseResult::EqualsNotProvided { |
1001 | arg: arg.to_string(), |
1002 | }) |
1003 | } |
1004 | } else if let Some(v) = attached_value { |
1005 | let arg_values = vec![v.to_owned()]; |
1006 | let trailing_idx = None; |
1007 | let react_result = ok!(self.react( |
1008 | Some(ident), |
1009 | ValueSource::CommandLine, |
1010 | arg, |
1011 | arg_values, |
1012 | trailing_idx, |
1013 | matcher, |
1014 | )); |
1015 | debug_assert_eq!(react_result, ParseResult::ValuesDone); |
1016 | // Attached are always done |
1017 | Ok(ParseResult::ValuesDone) |
1018 | } else { |
1019 | debug!("Parser::parse_opt_value: More arg vals required..." ); |
1020 | ok!(self.resolve_pending(matcher)); |
1021 | let trailing_values = false; |
1022 | matcher.pending_values_mut(arg.get_id(), Some(ident), trailing_values); |
1023 | Ok(ParseResult::Opt(arg.get_id().clone())) |
1024 | } |
1025 | } |
1026 | |
1027 | fn check_terminator(&self, arg: &Arg, val: &OsStr) -> Option<ParseResult> { |
1028 | if Some(val) == arg.terminator.as_ref().map(|s| OsStr::new(s.as_str())) { |
1029 | debug!("Parser::check_terminator: terminator={:?}" , arg.terminator); |
1030 | Some(ParseResult::ValuesDone) |
1031 | } else { |
1032 | None |
1033 | } |
1034 | } |
1035 | |
1036 | fn push_arg_values( |
1037 | &self, |
1038 | arg: &Arg, |
1039 | raw_vals: Vec<OsString>, |
1040 | source: ValueSource, |
1041 | matcher: &mut ArgMatcher, |
1042 | ) -> ClapResult<()> { |
1043 | debug!("Parser::push_arg_values: {raw_vals:?}" ); |
1044 | |
1045 | for raw_val in raw_vals { |
1046 | // update the current index because each value is a distinct index to clap |
1047 | self.cur_idx.set(self.cur_idx.get() + 1); |
1048 | debug!( |
1049 | "Parser::add_single_val_to_arg: cur_idx:={}" , |
1050 | self.cur_idx.get() |
1051 | ); |
1052 | let value_parser = arg.get_value_parser(); |
1053 | let val = ok!(value_parser.parse_ref(self.cmd, Some(arg), &raw_val, source)); |
1054 | |
1055 | matcher.add_val_to(arg.get_id(), val, raw_val); |
1056 | matcher.add_index_to(arg.get_id(), self.cur_idx.get()); |
1057 | } |
1058 | |
1059 | Ok(()) |
1060 | } |
1061 | |
1062 | fn resolve_pending(&self, matcher: &mut ArgMatcher) -> ClapResult<()> { |
1063 | let pending = match matcher.take_pending() { |
1064 | Some(pending) => pending, |
1065 | None => { |
1066 | return Ok(()); |
1067 | } |
1068 | }; |
1069 | |
1070 | debug!("Parser::resolve_pending: id={:?}" , pending.id); |
1071 | let arg = self.cmd.find(&pending.id).expect(INTERNAL_ERROR_MSG); |
1072 | let _ = ok!(self.react( |
1073 | pending.ident, |
1074 | ValueSource::CommandLine, |
1075 | arg, |
1076 | pending.raw_vals, |
1077 | pending.trailing_idx, |
1078 | matcher, |
1079 | )); |
1080 | |
1081 | Ok(()) |
1082 | } |
1083 | |
1084 | fn react( |
1085 | &self, |
1086 | ident: Option<Identifier>, |
1087 | source: ValueSource, |
1088 | arg: &Arg, |
1089 | mut raw_vals: Vec<OsString>, |
1090 | mut trailing_idx: Option<usize>, |
1091 | matcher: &mut ArgMatcher, |
1092 | ) -> ClapResult<ParseResult> { |
1093 | ok!(self.resolve_pending(matcher)); |
1094 | |
1095 | debug!( |
1096 | "Parser::react action={:?}, identifier={:?}, source={:?}" , |
1097 | arg.get_action(), |
1098 | ident, |
1099 | source |
1100 | ); |
1101 | |
1102 | // Process before `default_missing_values` to avoid it counting as values from the command |
1103 | // line |
1104 | if source == ValueSource::CommandLine { |
1105 | ok!(self.verify_num_args(arg, &raw_vals)); |
1106 | } |
1107 | |
1108 | if raw_vals.is_empty() { |
1109 | // We assume this case is valid: require equals, but min_vals == 0. |
1110 | if !arg.default_missing_vals.is_empty() { |
1111 | debug!("Parser::react: has default_missing_vals" ); |
1112 | trailing_idx = None; |
1113 | raw_vals.extend( |
1114 | arg.default_missing_vals |
1115 | .iter() |
1116 | .map(|s| s.as_os_str().to_owned()), |
1117 | ); |
1118 | } |
1119 | } |
1120 | |
1121 | if let Some(val_delim) = arg.get_value_delimiter() { |
1122 | if self.cmd.is_dont_delimit_trailing_values_set() && trailing_idx == Some(0) { |
1123 | // Nothing to do |
1124 | } else { |
1125 | let mut val_delim_buffer = [0; 4]; |
1126 | let val_delim = val_delim.encode_utf8(&mut val_delim_buffer); |
1127 | let mut split_raw_vals = Vec::with_capacity(raw_vals.len()); |
1128 | for (i, raw_val) in raw_vals.into_iter().enumerate() { |
1129 | if !raw_val.contains(val_delim) |
1130 | || (self.cmd.is_dont_delimit_trailing_values_set() |
1131 | && trailing_idx == Some(i)) |
1132 | { |
1133 | split_raw_vals.push(raw_val); |
1134 | } else { |
1135 | split_raw_vals.extend(raw_val.split(val_delim).map(|x| x.to_owned())); |
1136 | } |
1137 | } |
1138 | raw_vals = split_raw_vals |
1139 | } |
1140 | } |
1141 | |
1142 | match arg.get_action() { |
1143 | ArgAction::Set => { |
1144 | if source == ValueSource::CommandLine |
1145 | && matches!(ident, Some(Identifier::Short) | Some(Identifier::Long)) |
1146 | { |
1147 | // Record flag's index |
1148 | self.cur_idx.set(self.cur_idx.get() + 1); |
1149 | debug!("Parser::react: cur_idx:={}" , self.cur_idx.get()); |
1150 | } |
1151 | if matcher.remove(arg.get_id()) |
1152 | && !(self.cmd.is_args_override_self() || arg.overrides.contains(arg.get_id())) |
1153 | { |
1154 | return Err(ClapError::argument_conflict( |
1155 | self.cmd, |
1156 | arg.to_string(), |
1157 | vec![arg.to_string()], |
1158 | Usage::new(self.cmd).create_usage_with_title(&[]), |
1159 | )); |
1160 | } |
1161 | self.start_custom_arg(matcher, arg, source); |
1162 | ok!(self.push_arg_values(arg, raw_vals, source, matcher)); |
1163 | if cfg!(debug_assertions) && matcher.needs_more_vals(arg) { |
1164 | debug!( |
1165 | "Parser::react not enough values passed in, leaving it to the validator to complain" , |
1166 | ); |
1167 | } |
1168 | Ok(ParseResult::ValuesDone) |
1169 | } |
1170 | ArgAction::Append => { |
1171 | if source == ValueSource::CommandLine |
1172 | && matches!(ident, Some(Identifier::Short) | Some(Identifier::Long)) |
1173 | { |
1174 | // Record flag's index |
1175 | self.cur_idx.set(self.cur_idx.get() + 1); |
1176 | debug!("Parser::react: cur_idx:={}" , self.cur_idx.get()); |
1177 | } |
1178 | self.start_custom_arg(matcher, arg, source); |
1179 | ok!(self.push_arg_values(arg, raw_vals, source, matcher)); |
1180 | if cfg!(debug_assertions) && matcher.needs_more_vals(arg) { |
1181 | debug!( |
1182 | "Parser::react not enough values passed in, leaving it to the validator to complain" , |
1183 | ); |
1184 | } |
1185 | Ok(ParseResult::ValuesDone) |
1186 | } |
1187 | ArgAction::SetTrue => { |
1188 | let raw_vals = if raw_vals.is_empty() { |
1189 | vec![OsString::from("true" )] |
1190 | } else { |
1191 | raw_vals |
1192 | }; |
1193 | |
1194 | if matcher.remove(arg.get_id()) |
1195 | && !(self.cmd.is_args_override_self() || arg.overrides.contains(arg.get_id())) |
1196 | { |
1197 | return Err(ClapError::argument_conflict( |
1198 | self.cmd, |
1199 | arg.to_string(), |
1200 | vec![arg.to_string()], |
1201 | Usage::new(self.cmd).create_usage_with_title(&[]), |
1202 | )); |
1203 | } |
1204 | self.start_custom_arg(matcher, arg, source); |
1205 | ok!(self.push_arg_values(arg, raw_vals, source, matcher)); |
1206 | Ok(ParseResult::ValuesDone) |
1207 | } |
1208 | ArgAction::SetFalse => { |
1209 | let raw_vals = if raw_vals.is_empty() { |
1210 | vec![OsString::from("false" )] |
1211 | } else { |
1212 | raw_vals |
1213 | }; |
1214 | |
1215 | if matcher.remove(arg.get_id()) |
1216 | && !(self.cmd.is_args_override_self() || arg.overrides.contains(arg.get_id())) |
1217 | { |
1218 | return Err(ClapError::argument_conflict( |
1219 | self.cmd, |
1220 | arg.to_string(), |
1221 | vec![arg.to_string()], |
1222 | Usage::new(self.cmd).create_usage_with_title(&[]), |
1223 | )); |
1224 | } |
1225 | self.start_custom_arg(matcher, arg, source); |
1226 | ok!(self.push_arg_values(arg, raw_vals, source, matcher)); |
1227 | Ok(ParseResult::ValuesDone) |
1228 | } |
1229 | ArgAction::Count => { |
1230 | let raw_vals = if raw_vals.is_empty() { |
1231 | let existing_value = *matcher |
1232 | .get_one::<crate::builder::CountType>(arg.get_id().as_str()) |
1233 | .unwrap_or(&0); |
1234 | let next_value = existing_value.saturating_add(1); |
1235 | vec![OsString::from(next_value.to_string())] |
1236 | } else { |
1237 | raw_vals |
1238 | }; |
1239 | |
1240 | matcher.remove(arg.get_id()); |
1241 | self.start_custom_arg(matcher, arg, source); |
1242 | ok!(self.push_arg_values(arg, raw_vals, source, matcher)); |
1243 | Ok(ParseResult::ValuesDone) |
1244 | } |
1245 | ArgAction::Help => { |
1246 | let use_long = match ident { |
1247 | Some(Identifier::Long) => true, |
1248 | Some(Identifier::Short) => false, |
1249 | Some(Identifier::Index) => true, |
1250 | None => true, |
1251 | }; |
1252 | debug!("Help: use_long={use_long}" ); |
1253 | Err(self.help_err(use_long)) |
1254 | } |
1255 | ArgAction::HelpShort => { |
1256 | let use_long = false; |
1257 | debug!("Help: use_long={use_long}" ); |
1258 | Err(self.help_err(use_long)) |
1259 | } |
1260 | ArgAction::HelpLong => { |
1261 | let use_long = true; |
1262 | debug!("Help: use_long={use_long}" ); |
1263 | Err(self.help_err(use_long)) |
1264 | } |
1265 | ArgAction::Version => { |
1266 | let use_long = match ident { |
1267 | Some(Identifier::Long) => true, |
1268 | Some(Identifier::Short) => false, |
1269 | Some(Identifier::Index) => true, |
1270 | None => true, |
1271 | }; |
1272 | debug!("Version: use_long={use_long}" ); |
1273 | Err(self.version_err(use_long)) |
1274 | } |
1275 | } |
1276 | } |
1277 | |
1278 | fn verify_num_args(&self, arg: &Arg, raw_vals: &[OsString]) -> ClapResult<()> { |
1279 | if self.cmd.is_ignore_errors_set() { |
1280 | return Ok(()); |
1281 | } |
1282 | |
1283 | let actual = raw_vals.len(); |
1284 | let expected = arg.get_num_args().expect(INTERNAL_ERROR_MSG); |
1285 | |
1286 | if 0 < expected.min_values() && actual == 0 { |
1287 | // Issue 665 (https://github.com/clap-rs/clap/issues/665) |
1288 | // Issue 1105 (https://github.com/clap-rs/clap/issues/1105) |
1289 | return Err(ClapError::empty_value( |
1290 | self.cmd, |
1291 | &super::get_possible_values_cli(arg) |
1292 | .iter() |
1293 | .filter(|pv| !pv.is_hide_set()) |
1294 | .map(|n| n.get_name().to_owned()) |
1295 | .collect::<Vec<_>>(), |
1296 | arg.to_string(), |
1297 | )); |
1298 | } else if let Some(expected) = expected.num_values() { |
1299 | if expected != actual { |
1300 | debug!("Validator::validate_arg_num_vals: Sending error WrongNumberOfValues" ); |
1301 | return Err(ClapError::wrong_number_of_values( |
1302 | self.cmd, |
1303 | arg.to_string(), |
1304 | expected, |
1305 | actual, |
1306 | Usage::new(self.cmd).create_usage_with_title(&[]), |
1307 | )); |
1308 | } |
1309 | } else if actual < expected.min_values() { |
1310 | return Err(ClapError::too_few_values( |
1311 | self.cmd, |
1312 | arg.to_string(), |
1313 | expected.min_values(), |
1314 | actual, |
1315 | Usage::new(self.cmd).create_usage_with_title(&[]), |
1316 | )); |
1317 | } else if expected.max_values() < actual { |
1318 | debug!("Validator::validate_arg_num_vals: Sending error TooManyValues" ); |
1319 | return Err(ClapError::too_many_values( |
1320 | self.cmd, |
1321 | raw_vals |
1322 | .last() |
1323 | .expect(INTERNAL_ERROR_MSG) |
1324 | .to_string_lossy() |
1325 | .into_owned(), |
1326 | arg.to_string(), |
1327 | Usage::new(self.cmd).create_usage_with_title(&[]), |
1328 | )); |
1329 | } |
1330 | |
1331 | Ok(()) |
1332 | } |
1333 | |
1334 | fn remove_overrides(&self, arg: &Arg, matcher: &mut ArgMatcher) { |
1335 | debug!("Parser::remove_overrides: id={:?}" , arg.id); |
1336 | for override_id in &arg.overrides { |
1337 | debug!("Parser::remove_overrides:iter:{override_id:?}: removing" ); |
1338 | matcher.remove(override_id); |
1339 | } |
1340 | |
1341 | // Override anything that can override us |
1342 | let mut transitive = Vec::new(); |
1343 | for arg_id in matcher.arg_ids() { |
1344 | if let Some(overrider) = self.cmd.find(arg_id) { |
1345 | if overrider.overrides.contains(arg.get_id()) { |
1346 | transitive.push(overrider.get_id()); |
1347 | } |
1348 | } |
1349 | } |
1350 | for overrider_id in transitive { |
1351 | debug!("Parser::remove_overrides:iter:{overrider_id:?}: removing" ); |
1352 | matcher.remove(overrider_id); |
1353 | } |
1354 | } |
1355 | |
1356 | #[cfg (feature = "env" )] |
1357 | fn add_env(&mut self, matcher: &mut ArgMatcher) -> ClapResult<()> { |
1358 | debug!("Parser::add_env" ); |
1359 | |
1360 | for arg in self.cmd.get_arguments() { |
1361 | // Use env only if the arg was absent among command line args, |
1362 | // early return if this is not the case. |
1363 | if matcher.contains(&arg.id) { |
1364 | debug!("Parser::add_env: Skipping existing arg `{arg}`" ); |
1365 | continue; |
1366 | } |
1367 | |
1368 | debug!("Parser::add_env: Checking arg `{arg}`" ); |
1369 | if let Some((_, Some(ref val))) = arg.env { |
1370 | debug!("Parser::add_env: Found an opt with value={val:?}" ); |
1371 | let arg_values = vec![val.to_owned()]; |
1372 | let trailing_idx = None; |
1373 | let _ = ok!(self.react( |
1374 | None, |
1375 | ValueSource::EnvVariable, |
1376 | arg, |
1377 | arg_values, |
1378 | trailing_idx, |
1379 | matcher, |
1380 | )); |
1381 | } |
1382 | } |
1383 | |
1384 | Ok(()) |
1385 | } |
1386 | |
1387 | fn add_defaults(&self, matcher: &mut ArgMatcher) -> ClapResult<()> { |
1388 | debug!("Parser::add_defaults" ); |
1389 | |
1390 | for arg in self.cmd.get_arguments() { |
1391 | debug!("Parser::add_defaults:iter:{}:" , arg.get_id()); |
1392 | ok!(self.add_default_value(arg, matcher)); |
1393 | } |
1394 | |
1395 | Ok(()) |
1396 | } |
1397 | |
1398 | fn add_default_value(&self, arg: &Arg, matcher: &mut ArgMatcher) -> ClapResult<()> { |
1399 | if !arg.default_vals_ifs.is_empty() { |
1400 | debug!("Parser::add_default_value: has conditional defaults" ); |
1401 | if !matcher.contains(arg.get_id()) { |
1402 | for (id, val, default) in arg.default_vals_ifs.iter() { |
1403 | let add = if let Some(a) = matcher.get(id) { |
1404 | match val { |
1405 | crate::builder::ArgPredicate::Equals(v) => { |
1406 | a.raw_vals_flatten().any(|value| v == value) |
1407 | } |
1408 | crate::builder::ArgPredicate::IsPresent => true, |
1409 | } |
1410 | } else { |
1411 | false |
1412 | }; |
1413 | |
1414 | if add { |
1415 | if let Some(default) = default { |
1416 | let arg_values = vec![default.to_os_string()]; |
1417 | let trailing_idx = None; |
1418 | let _ = ok!(self.react( |
1419 | None, |
1420 | ValueSource::DefaultValue, |
1421 | arg, |
1422 | arg_values, |
1423 | trailing_idx, |
1424 | matcher, |
1425 | )); |
1426 | } |
1427 | return Ok(()); |
1428 | } |
1429 | } |
1430 | } |
1431 | } else { |
1432 | debug!("Parser::add_default_value: doesn't have conditional defaults" ); |
1433 | } |
1434 | |
1435 | if !arg.default_vals.is_empty() { |
1436 | debug!( |
1437 | "Parser::add_default_value:iter:{}: has default vals" , |
1438 | arg.get_id() |
1439 | ); |
1440 | if matcher.contains(arg.get_id()) { |
1441 | debug!("Parser::add_default_value:iter:{}: was used" , arg.get_id()); |
1442 | // do nothing |
1443 | } else { |
1444 | debug!( |
1445 | "Parser::add_default_value:iter:{}: wasn't used" , |
1446 | arg.get_id() |
1447 | ); |
1448 | let arg_values: Vec<_> = arg |
1449 | .default_vals |
1450 | .iter() |
1451 | .map(crate::builder::OsStr::to_os_string) |
1452 | .collect(); |
1453 | let trailing_idx = None; |
1454 | let _ = ok!(self.react( |
1455 | None, |
1456 | ValueSource::DefaultValue, |
1457 | arg, |
1458 | arg_values, |
1459 | trailing_idx, |
1460 | matcher, |
1461 | )); |
1462 | } |
1463 | } else { |
1464 | debug!( |
1465 | "Parser::add_default_value:iter:{}: doesn't have default vals" , |
1466 | arg.get_id() |
1467 | ); |
1468 | |
1469 | // do nothing |
1470 | } |
1471 | |
1472 | Ok(()) |
1473 | } |
1474 | |
1475 | fn start_custom_arg(&self, matcher: &mut ArgMatcher, arg: &Arg, source: ValueSource) { |
1476 | if source == ValueSource::CommandLine { |
1477 | // With each new occurrence, remove overrides from prior occurrences |
1478 | self.remove_overrides(arg, matcher); |
1479 | } |
1480 | matcher.start_custom_arg(arg, source); |
1481 | if source.is_explicit() { |
1482 | for group in self.cmd.groups_for_arg(arg.get_id()) { |
1483 | matcher.start_custom_group(group.clone(), source); |
1484 | matcher.add_val_to( |
1485 | &group, |
1486 | AnyValue::new(arg.get_id().clone()), |
1487 | OsString::from(arg.get_id().as_str()), |
1488 | ); |
1489 | } |
1490 | } |
1491 | } |
1492 | } |
1493 | |
1494 | // Error, Help, and Version Methods |
1495 | impl<'cmd> Parser<'cmd> { |
1496 | /// Is only used for the long flag(which is the only one needs fuzzy searching) |
1497 | fn did_you_mean_error( |
1498 | &mut self, |
1499 | arg: &str, |
1500 | matcher: &mut ArgMatcher, |
1501 | remaining_args: &[&OsStr], |
1502 | trailing_values: bool, |
1503 | ) -> ClapError { |
1504 | debug!("Parser::did_you_mean_error: arg={arg}" ); |
1505 | // Didn't match a flag or option |
1506 | let longs = self |
1507 | .cmd |
1508 | .get_keymap() |
1509 | .keys() |
1510 | .filter_map(|x| match x { |
1511 | KeyType::Long(l) => Some(l.to_string_lossy().into_owned()), |
1512 | _ => None, |
1513 | }) |
1514 | .collect::<Vec<_>>(); |
1515 | debug!("Parser::did_you_mean_error: longs={longs:?}" ); |
1516 | |
1517 | let did_you_mean = suggestions::did_you_mean_flag( |
1518 | arg, |
1519 | remaining_args, |
1520 | longs.iter().map(|x| &x[..]), |
1521 | self.cmd.get_subcommands_mut(), |
1522 | ); |
1523 | |
1524 | // Add the arg to the matches to build a proper usage string |
1525 | if let Some((name, _)) = did_you_mean.as_ref() { |
1526 | if let Some(arg) = self.cmd.get_keymap().get(&name.as_ref()) { |
1527 | self.start_custom_arg(matcher, arg, ValueSource::CommandLine); |
1528 | } |
1529 | } |
1530 | let did_you_mean = did_you_mean.map(|(arg, cmd)| (format!("-- {arg}" ), cmd)); |
1531 | |
1532 | let required = self.cmd.required_graph(); |
1533 | let used: Vec<Id> = matcher |
1534 | .arg_ids() |
1535 | .filter(|arg_id| { |
1536 | matcher.check_explicit(arg_id, &crate::builder::ArgPredicate::IsPresent) |
1537 | }) |
1538 | .filter(|n| self.cmd.find(n).map(|a| !a.is_hide_set()).unwrap_or(true)) |
1539 | .cloned() |
1540 | .collect(); |
1541 | |
1542 | // `did_you_mean` is a lot more likely and should cause us to skip the `--` suggestion |
1543 | // |
1544 | // In theory, this is only called for `--long`s, so we don't need to check |
1545 | let suggested_trailing_arg = |
1546 | did_you_mean.is_none() && !trailing_values && self.cmd.has_positionals(); |
1547 | ClapError::unknown_argument( |
1548 | self.cmd, |
1549 | format!("-- {arg}" ), |
1550 | did_you_mean, |
1551 | suggested_trailing_arg, |
1552 | Usage::new(self.cmd) |
1553 | .required(&required) |
1554 | .create_usage_with_title(&used), |
1555 | ) |
1556 | } |
1557 | |
1558 | fn help_err(&self, use_long: bool) -> ClapError { |
1559 | let styled = self.cmd.write_help_err(use_long); |
1560 | ClapError::display_help(self.cmd, styled) |
1561 | } |
1562 | |
1563 | fn version_err(&self, use_long: bool) -> ClapError { |
1564 | let styled = self.cmd.write_version_err(use_long); |
1565 | ClapError::display_version(self.cmd, styled) |
1566 | } |
1567 | } |
1568 | |
1569 | #[derive (Debug, PartialEq, Eq)] |
1570 | pub(crate) enum ParseState { |
1571 | ValuesDone, |
1572 | Opt(Id), |
1573 | Pos(Id), |
1574 | } |
1575 | |
1576 | /// Recoverable Parsing results. |
1577 | #[derive (Debug, PartialEq, Clone)] |
1578 | #[must_use ] |
1579 | enum ParseResult { |
1580 | FlagSubCommand(String), |
1581 | Opt(Id), |
1582 | ValuesDone, |
1583 | /// Value attached to the short flag is not consumed(e.g. 'u' for `-cu` is |
1584 | /// not consumed). |
1585 | AttachedValueNotConsumed, |
1586 | /// This long flag doesn't need a value but is provided one. |
1587 | UnneededAttachedValue { |
1588 | rest: String, |
1589 | used: Vec<Id>, |
1590 | arg: String, |
1591 | }, |
1592 | /// This flag might be an hyphen Value. |
1593 | MaybeHyphenValue, |
1594 | /// Equals required but not provided. |
1595 | EqualsNotProvided { |
1596 | arg: String, |
1597 | }, |
1598 | /// Failed to match a Arg. |
1599 | NoMatchingArg { |
1600 | arg: String, |
1601 | }, |
1602 | /// No argument found e.g. parser is given `-` when parsing a flag. |
1603 | NoArg, |
1604 | } |
1605 | |
1606 | #[derive (Clone, Debug, PartialEq, Eq)] |
1607 | pub(crate) struct PendingArg { |
1608 | pub(crate) id: Id, |
1609 | pub(crate) ident: Option<Identifier>, |
1610 | pub(crate) raw_vals: Vec<OsString>, |
1611 | pub(crate) trailing_idx: Option<usize>, |
1612 | } |
1613 | |
1614 | #[derive (Copy, Clone, Debug, PartialEq, Eq)] |
1615 | pub(crate) enum Identifier { |
1616 | Short, |
1617 | Long, |
1618 | Index, |
1619 | } |
1620 | |