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