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( |
448 | &arg_os, |
449 | valid_arg_found, |
450 | trailing_values, |
451 | matcher, |
452 | )); |
453 | } |
454 | } |
455 | |
456 | if let Some(ref pos_sc_name) = subcmd_name { |
457 | if self.cmd.is_args_conflicts_with_subcommands_set() && valid_arg_found { |
458 | return Err(ClapError::subcommand_conflict( |
459 | self.cmd, |
460 | pos_sc_name.clone(), |
461 | matcher |
462 | .arg_ids() |
463 | .map(|id| self.cmd.find(id).unwrap().to_string()) |
464 | .collect(), |
465 | Usage::new(self.cmd).create_usage_with_title(&[]), |
466 | )); |
467 | } |
468 | let sc_name = self |
469 | .cmd |
470 | .find_subcommand(pos_sc_name) |
471 | .expect(INTERNAL_ERROR_MSG) |
472 | .get_name() |
473 | .to_owned(); |
474 | ok!(self.parse_subcommand(&sc_name, matcher, raw_args, args_cursor, keep_state)); |
475 | } |
476 | |
477 | ok!(self.resolve_pending(matcher)); |
478 | #[cfg (feature = "env" )] |
479 | ok!(self.add_env(matcher)); |
480 | ok!(self.add_defaults(matcher)); |
481 | Validator::new(self.cmd).validate(parse_state, matcher) |
482 | } |
483 | |
484 | fn match_arg_error( |
485 | &self, |
486 | arg_os: &clap_lex::ParsedArg<'_>, |
487 | valid_arg_found: bool, |
488 | trailing_values: bool, |
489 | matcher: &ArgMatcher, |
490 | ) -> ClapError { |
491 | // If argument follows a `--` |
492 | if trailing_values { |
493 | // If the arg matches a subcommand name, or any of its aliases (if defined) |
494 | if self |
495 | .possible_subcommand(arg_os.to_value(), valid_arg_found) |
496 | .is_some() |
497 | { |
498 | return ClapError::unnecessary_double_dash( |
499 | self.cmd, |
500 | arg_os.display().to_string(), |
501 | Usage::new(self.cmd).create_usage_with_title(&[]), |
502 | ); |
503 | } |
504 | } |
505 | |
506 | let suggested_trailing_arg = !trailing_values |
507 | && self.cmd.has_positionals() |
508 | && (arg_os.is_long() || arg_os.is_short()); |
509 | |
510 | if self.cmd.has_subcommands() { |
511 | if self.cmd.is_args_conflicts_with_subcommands_set() && valid_arg_found { |
512 | return ClapError::subcommand_conflict( |
513 | self.cmd, |
514 | arg_os.display().to_string(), |
515 | matcher |
516 | .arg_ids() |
517 | .filter_map(|id| self.cmd.find(id).map(|a| a.to_string())) |
518 | .collect(), |
519 | Usage::new(self.cmd).create_usage_with_title(&[]), |
520 | ); |
521 | } |
522 | |
523 | let candidates = suggestions::did_you_mean( |
524 | &arg_os.display().to_string(), |
525 | self.cmd.all_subcommand_names(), |
526 | ); |
527 | // If the argument looks like a subcommand. |
528 | if !candidates.is_empty() { |
529 | return ClapError::invalid_subcommand( |
530 | self.cmd, |
531 | arg_os.display().to_string(), |
532 | candidates, |
533 | self.cmd.get_bin_name_fallback().to_owned(), |
534 | suggested_trailing_arg, |
535 | Usage::new(self.cmd).create_usage_with_title(&[]), |
536 | ); |
537 | } |
538 | |
539 | // If the argument must be a subcommand. |
540 | if !self.cmd.has_positionals() || self.cmd.is_infer_subcommands_set() { |
541 | return ClapError::unrecognized_subcommand( |
542 | self.cmd, |
543 | arg_os.display().to_string(), |
544 | Usage::new(self.cmd).create_usage_with_title(&[]), |
545 | ); |
546 | } |
547 | } |
548 | |
549 | ClapError::unknown_argument( |
550 | self.cmd, |
551 | arg_os.display().to_string(), |
552 | None, |
553 | suggested_trailing_arg, |
554 | Usage::new(self.cmd).create_usage_with_title(&[]), |
555 | ) |
556 | } |
557 | |
558 | // Checks if the arg matches a subcommand name, or any of its aliases (if defined) |
559 | fn possible_subcommand( |
560 | &self, |
561 | arg: Result<&str, &OsStr>, |
562 | valid_arg_found: bool, |
563 | ) -> Option<&str> { |
564 | debug!("Parser::possible_subcommand: arg={arg:?}" ); |
565 | let arg = some!(arg.ok()); |
566 | |
567 | if !(self.cmd.is_args_conflicts_with_subcommands_set() && valid_arg_found) { |
568 | if self.cmd.is_infer_subcommands_set() { |
569 | // For subcommand `test`, we accepts it's prefix: `t`, `te`, |
570 | // `tes` and `test`. |
571 | let mut iter = self.cmd.get_subcommands().filter_map(|s| { |
572 | if s.get_name().starts_with(arg) { |
573 | return Some(s.get_name()); |
574 | } |
575 | |
576 | // Use find here instead of chaining the iterator because we want to accept |
577 | // conflicts in aliases. |
578 | s.get_all_aliases().find(|s| s.starts_with(arg)) |
579 | }); |
580 | |
581 | if let name @ Some(_) = iter.next() { |
582 | if iter.next().is_none() { |
583 | return name; |
584 | } |
585 | } |
586 | } |
587 | // Don't use an else here because we want inference to support exact matching even if |
588 | // there are conflicts. |
589 | if let Some(sc) = self.cmd.find_subcommand(arg) { |
590 | return Some(sc.get_name()); |
591 | } |
592 | } |
593 | None |
594 | } |
595 | |
596 | // Checks if the arg matches a long flag subcommand name, or any of its aliases (if defined) |
597 | fn possible_long_flag_subcommand(&self, arg: &str) -> Option<&str> { |
598 | debug!("Parser::possible_long_flag_subcommand: arg={arg:?}" ); |
599 | if self.cmd.is_infer_subcommands_set() { |
600 | let mut iter = self.cmd.get_subcommands().filter_map(|sc| { |
601 | sc.get_long_flag().and_then(|long| { |
602 | if long.starts_with(arg) { |
603 | Some(sc.get_name()) |
604 | } else { |
605 | sc.get_all_long_flag_aliases().find_map(|alias| { |
606 | if alias.starts_with(arg) { |
607 | Some(sc.get_name()) |
608 | } else { |
609 | None |
610 | } |
611 | }) |
612 | } |
613 | }) |
614 | }); |
615 | |
616 | if let name @ Some(_) = iter.next() { |
617 | if iter.next().is_none() { |
618 | return name; |
619 | } |
620 | } |
621 | } |
622 | if let Some(sc_name) = self.cmd.find_long_subcmd(arg) { |
623 | return Some(sc_name); |
624 | } |
625 | None |
626 | } |
627 | |
628 | fn parse_help_subcommand( |
629 | &self, |
630 | cmds: impl Iterator<Item = &'cmd OsStr>, |
631 | ) -> ClapResult<std::convert::Infallible> { |
632 | debug!("Parser::parse_help_subcommand" ); |
633 | |
634 | let mut cmd = self.cmd.clone(); |
635 | let sc = { |
636 | let mut sc = &mut cmd; |
637 | |
638 | for cmd in cmds { |
639 | sc = if let Some(sc_name) = |
640 | sc.find_subcommand(cmd).map(|sc| sc.get_name().to_owned()) |
641 | { |
642 | sc._build_subcommand(&sc_name).unwrap() |
643 | } else { |
644 | return Err(ClapError::unrecognized_subcommand( |
645 | sc, |
646 | cmd.to_string_lossy().into_owned(), |
647 | Usage::new(sc).create_usage_with_title(&[]), |
648 | )); |
649 | }; |
650 | } |
651 | |
652 | sc |
653 | }; |
654 | let parser = Parser::new(sc); |
655 | |
656 | Err(parser.help_err(true)) |
657 | } |
658 | |
659 | fn is_new_arg(&self, next: &clap_lex::ParsedArg<'_>, current_positional: &Arg) -> bool { |
660 | #![allow (clippy::needless_bool)] // Prefer consistent if/else-if ladder |
661 | |
662 | debug!( |
663 | "Parser::is_new_arg: {:?}:{}" , |
664 | next.to_value_os(), |
665 | current_positional.get_id() |
666 | ); |
667 | |
668 | if self.cmd[current_positional.get_id()].is_allow_hyphen_values_set() |
669 | || (self.cmd[current_positional.get_id()].is_allow_negative_numbers_set() |
670 | && next.is_negative_number()) |
671 | { |
672 | // If allow hyphen, this isn't a new arg. |
673 | debug!("Parser::is_new_arg: Allow hyphen" ); |
674 | false |
675 | } else if next.is_long() { |
676 | // If this is a long flag, this is a new arg. |
677 | debug!("Parser::is_new_arg: --<something> found" ); |
678 | true |
679 | } else if next.is_short() { |
680 | // If this is a short flag, this is a new arg. But a singe '-' by |
681 | // itself is a value and typically means "stdin" on unix systems. |
682 | debug!("Parser::is_new_arg: -<something> found" ); |
683 | true |
684 | } else { |
685 | // Nothing special, this is a value. |
686 | debug!("Parser::is_new_arg: value" ); |
687 | false |
688 | } |
689 | } |
690 | |
691 | fn parse_subcommand( |
692 | &mut self, |
693 | sc_name: &str, |
694 | matcher: &mut ArgMatcher, |
695 | raw_args: &mut clap_lex::RawArgs, |
696 | args_cursor: clap_lex::ArgCursor, |
697 | keep_state: bool, |
698 | ) -> ClapResult<()> { |
699 | debug!("Parser::parse_subcommand" ); |
700 | |
701 | let partial_parsing_enabled = self.cmd.is_ignore_errors_set(); |
702 | |
703 | if let Some(sc) = self.cmd._build_subcommand(sc_name) { |
704 | let mut sc_matcher = ArgMatcher::new(sc); |
705 | |
706 | debug!( |
707 | "Parser::parse_subcommand: About to parse sc={}" , |
708 | sc.get_name() |
709 | ); |
710 | |
711 | { |
712 | let mut p = Parser::new(sc); |
713 | // HACK: maintain indexes between parsers |
714 | // FlagSubCommand short arg needs to revisit the current short args, but skip the subcommand itself |
715 | if keep_state { |
716 | p.cur_idx.set(self.cur_idx.get()); |
717 | p.flag_subcmd_at = self.flag_subcmd_at; |
718 | p.flag_subcmd_skip = self.flag_subcmd_skip; |
719 | } |
720 | if let Err(error) = p.get_matches_with(&mut sc_matcher, raw_args, args_cursor) { |
721 | if partial_parsing_enabled { |
722 | debug!("Parser::parse_subcommand: ignored error in subcommand {sc_name}: {error:?}" ); |
723 | } else { |
724 | return Err(error); |
725 | } |
726 | } |
727 | } |
728 | matcher.subcommand(SubCommand { |
729 | name: sc.get_name().to_owned(), |
730 | matches: sc_matcher.into_inner(), |
731 | }); |
732 | } |
733 | Ok(()) |
734 | } |
735 | |
736 | fn parse_long_arg( |
737 | &mut self, |
738 | matcher: &mut ArgMatcher, |
739 | long_arg: Result<&str, &OsStr>, |
740 | long_value: Option<&OsStr>, |
741 | parse_state: &ParseState, |
742 | pos_counter: usize, |
743 | valid_arg_found: &mut bool, |
744 | ) -> ClapResult<ParseResult> { |
745 | // maybe here lifetime should be 'a |
746 | debug!("Parser::parse_long_arg" ); |
747 | |
748 | #[allow (clippy::blocks_in_if_conditions)] |
749 | if matches!(parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) if |
750 | self.cmd[opt].is_allow_hyphen_values_set()) |
751 | { |
752 | debug!("Parser::parse_long_arg: prior arg accepts hyphenated values" ,); |
753 | return Ok(ParseResult::MaybeHyphenValue); |
754 | } |
755 | |
756 | debug!("Parser::parse_long_arg: Does it contain '='..." ); |
757 | let long_arg = match long_arg { |
758 | Ok(long_arg) => long_arg, |
759 | Err(long_arg_os) => { |
760 | return Ok(ParseResult::NoMatchingArg { |
761 | arg: long_arg_os.to_string_lossy().into_owned(), |
762 | }) |
763 | } |
764 | }; |
765 | if long_arg.is_empty() { |
766 | debug_assert!( |
767 | long_value.is_some(), |
768 | "`--` should be filtered out before this point" |
769 | ); |
770 | } |
771 | |
772 | let arg = if let Some(arg) = self.cmd.get_keymap().get(long_arg) { |
773 | debug!("Parser::parse_long_arg: Found valid arg or flag '{arg}'" ); |
774 | Some((long_arg, arg)) |
775 | } else if self.cmd.is_infer_long_args_set() { |
776 | let mut iter = self.cmd.get_arguments().filter_map(|a| { |
777 | if let Some(long) = a.get_long() { |
778 | if long.starts_with(long_arg) { |
779 | return Some((long, a)); |
780 | } |
781 | } |
782 | a.aliases |
783 | .iter() |
784 | .find_map(|(alias, _)| alias.starts_with(long_arg).then(|| (alias.as_str(), a))) |
785 | }); |
786 | |
787 | iter.next().filter(|_| iter.next().is_none()) |
788 | } else { |
789 | None |
790 | }; |
791 | |
792 | if let Some((_long_arg, arg)) = arg { |
793 | let ident = Identifier::Long; |
794 | *valid_arg_found = true; |
795 | if arg.is_takes_value_set() { |
796 | debug!( |
797 | "Parser::parse_long_arg({:?}): Found an arg with value '{:?}'" , |
798 | long_arg, &long_value |
799 | ); |
800 | let has_eq = long_value.is_some(); |
801 | self.parse_opt_value(ident, long_value, arg, matcher, has_eq) |
802 | } else if let Some(rest) = long_value { |
803 | let required = self.cmd.required_graph(); |
804 | debug!("Parser::parse_long_arg({long_arg:?}): Got invalid literal `{rest:?}`" ); |
805 | let mut used: Vec<Id> = matcher |
806 | .arg_ids() |
807 | .filter(|arg_id| { |
808 | matcher.check_explicit(arg_id, &crate::builder::ArgPredicate::IsPresent) |
809 | }) |
810 | .filter(|&n| { |
811 | self.cmd |
812 | .find(n) |
813 | .map(|a| !(a.is_hide_set() || required.contains(a.get_id()))) |
814 | .unwrap_or(true) |
815 | }) |
816 | .cloned() |
817 | .collect(); |
818 | used.push(arg.get_id().clone()); |
819 | |
820 | Ok(ParseResult::UnneededAttachedValue { |
821 | rest: rest.to_string_lossy().into_owned(), |
822 | used, |
823 | arg: arg.to_string(), |
824 | }) |
825 | } else { |
826 | debug!("Parser::parse_long_arg({long_arg:?}): Presence validated" ); |
827 | let trailing_idx = None; |
828 | self.react( |
829 | Some(ident), |
830 | ValueSource::CommandLine, |
831 | arg, |
832 | vec![], |
833 | trailing_idx, |
834 | matcher, |
835 | ) |
836 | } |
837 | } else if let Some(sc_name) = self.possible_long_flag_subcommand(long_arg) { |
838 | Ok(ParseResult::FlagSubCommand(sc_name.to_string())) |
839 | } else if self |
840 | .cmd |
841 | .get_keymap() |
842 | .get(&pos_counter) |
843 | .map(|arg| arg.is_allow_hyphen_values_set() && !arg.is_last_set()) |
844 | .unwrap_or_default() |
845 | { |
846 | debug!("Parser::parse_long_args: positional at {pos_counter} allows hyphens" ); |
847 | Ok(ParseResult::MaybeHyphenValue) |
848 | } else { |
849 | Ok(ParseResult::NoMatchingArg { |
850 | arg: long_arg.to_owned(), |
851 | }) |
852 | } |
853 | } |
854 | |
855 | fn parse_short_arg( |
856 | &mut self, |
857 | matcher: &mut ArgMatcher, |
858 | mut short_arg: clap_lex::ShortFlags<'_>, |
859 | parse_state: &ParseState, |
860 | // change this to possible pos_arg when removing the usage of &mut Parser. |
861 | pos_counter: usize, |
862 | valid_arg_found: &mut bool, |
863 | ) -> ClapResult<ParseResult> { |
864 | debug!("Parser::parse_short_arg: short_arg={short_arg:?}" ); |
865 | |
866 | #[allow (clippy::blocks_in_if_conditions)] |
867 | if matches!(parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) |
868 | if self.cmd[opt].is_allow_hyphen_values_set() || (self.cmd[opt].is_allow_negative_numbers_set() && short_arg.is_negative_number())) |
869 | { |
870 | debug!("Parser::parse_short_args: prior arg accepts hyphenated values" ,); |
871 | return Ok(ParseResult::MaybeHyphenValue); |
872 | } else if self |
873 | .cmd |
874 | .get_keymap() |
875 | .get(&pos_counter) |
876 | .map(|arg| arg.is_allow_negative_numbers_set()) |
877 | .unwrap_or_default() |
878 | && short_arg.is_negative_number() |
879 | { |
880 | debug!("Parser::parse_short_arg: negative number" ); |
881 | return Ok(ParseResult::MaybeHyphenValue); |
882 | } else if self |
883 | .cmd |
884 | .get_keymap() |
885 | .get(&pos_counter) |
886 | .map(|arg| arg.is_allow_hyphen_values_set() && !arg.is_last_set()) |
887 | .unwrap_or_default() |
888 | && short_arg |
889 | .clone() |
890 | .any(|c| !c.map(|c| self.cmd.contains_short(c)).unwrap_or_default()) |
891 | { |
892 | debug!("Parser::parse_short_args: positional at {pos_counter} allows hyphens" ); |
893 | return Ok(ParseResult::MaybeHyphenValue); |
894 | } |
895 | |
896 | let mut ret = ParseResult::NoArg; |
897 | |
898 | let skip = self.flag_subcmd_skip; |
899 | self.flag_subcmd_skip = 0; |
900 | let res = short_arg.advance_by(skip); |
901 | debug_assert_eq!( |
902 | res, |
903 | Ok(()), |
904 | "tracking of `flag_subcmd_skip` is off for ` {short_arg:?}`" |
905 | ); |
906 | while let Some(c) = short_arg.next_flag() { |
907 | let c = match c { |
908 | Ok(c) => c, |
909 | Err(rest) => { |
910 | return Ok(ParseResult::NoMatchingArg { |
911 | arg: format!("- {}" , rest.to_string_lossy()), |
912 | }); |
913 | } |
914 | }; |
915 | debug!("Parser::parse_short_arg:iter:{c}" ); |
916 | |
917 | // Check for matching short options, and return the name if there is no trailing |
918 | // concatenated value: -oval |
919 | // Option: -o |
920 | // Value: val |
921 | if let Some(arg) = self.cmd.get_keymap().get(&c) { |
922 | let ident = Identifier::Short; |
923 | debug!("Parser::parse_short_arg:iter:{c}: Found valid opt or flag" ); |
924 | *valid_arg_found = true; |
925 | if !arg.is_takes_value_set() { |
926 | let arg_values = Vec::new(); |
927 | let trailing_idx = None; |
928 | ret = ok!(self.react( |
929 | Some(ident), |
930 | ValueSource::CommandLine, |
931 | arg, |
932 | arg_values, |
933 | trailing_idx, |
934 | matcher, |
935 | )); |
936 | continue; |
937 | } |
938 | |
939 | // Check for trailing concatenated value |
940 | // |
941 | // Cloning the iterator, so we rollback if it isn't there. |
942 | let val = short_arg.clone().next_value_os().unwrap_or_default(); |
943 | debug!("Parser::parse_short_arg:iter:{c}: val={val:?}, short_arg={short_arg:?}" ); |
944 | let val = Some(val).filter(|v| !v.is_empty()); |
945 | |
946 | // Default to "we're expecting a value later". |
947 | // |
948 | // If attached value is not consumed, we may have more short |
949 | // flags to parse, continue. |
950 | // |
951 | // e.g. `-xvf`, when require_equals && x.min_vals == 0, we don't |
952 | // consume the `vf`, even if it's provided as value. |
953 | let (val, has_eq) = if let Some(val) = val.and_then(|v| v.strip_prefix("=" )) { |
954 | (Some(val), true) |
955 | } else { |
956 | (val, false) |
957 | }; |
958 | match ok!(self.parse_opt_value(ident, val, arg, matcher, has_eq)) { |
959 | ParseResult::AttachedValueNotConsumed => continue, |
960 | x => return Ok(x), |
961 | } |
962 | } |
963 | |
964 | return if let Some(sc_name) = self.cmd.find_short_subcmd(c) { |
965 | debug!("Parser::parse_short_arg:iter:{c}: subcommand={sc_name}" ); |
966 | // Make sure indices get updated before reading `self.cur_idx` |
967 | ok!(self.resolve_pending(matcher)); |
968 | self.cur_idx.set(self.cur_idx.get() + 1); |
969 | debug!("Parser::parse_short_arg: cur_idx:={}" , self.cur_idx.get()); |
970 | |
971 | let name = sc_name.to_string(); |
972 | // Get the index of the previously saved flag subcommand in the group of flags (if exists). |
973 | // If it is a new flag subcommand, then the formentioned index should be the current one |
974 | // (ie. `cur_idx`), and should be registered. |
975 | let cur_idx = self.cur_idx.get(); |
976 | self.flag_subcmd_at.get_or_insert(cur_idx); |
977 | let done_short_args = short_arg.is_empty(); |
978 | if done_short_args { |
979 | self.flag_subcmd_at = None; |
980 | } |
981 | Ok(ParseResult::FlagSubCommand(name)) |
982 | } else { |
983 | Ok(ParseResult::NoMatchingArg { |
984 | arg: format!("- {c}" ), |
985 | }) |
986 | }; |
987 | } |
988 | Ok(ret) |
989 | } |
990 | |
991 | fn parse_opt_value( |
992 | &self, |
993 | ident: Identifier, |
994 | attached_value: Option<&OsStr>, |
995 | arg: &Arg, |
996 | matcher: &mut ArgMatcher, |
997 | has_eq: bool, |
998 | ) -> ClapResult<ParseResult> { |
999 | debug!( |
1000 | "Parser::parse_opt_value; arg={}, val={:?}, has_eq={:?}" , |
1001 | arg.get_id(), |
1002 | attached_value, |
1003 | has_eq |
1004 | ); |
1005 | debug!("Parser::parse_opt_value; arg.settings={:?}" , arg.settings); |
1006 | |
1007 | debug!("Parser::parse_opt_value; Checking for val..." ); |
1008 | // require_equals is set, but no '=' is provided, try throwing error. |
1009 | if arg.is_require_equals_set() && !has_eq { |
1010 | if arg.get_min_vals() == 0 { |
1011 | debug!("Requires equals, but min_vals == 0" ); |
1012 | let arg_values = Vec::new(); |
1013 | let trailing_idx = None; |
1014 | let react_result = ok!(self.react( |
1015 | Some(ident), |
1016 | ValueSource::CommandLine, |
1017 | arg, |
1018 | arg_values, |
1019 | trailing_idx, |
1020 | matcher, |
1021 | )); |
1022 | debug_assert_eq!(react_result, ParseResult::ValuesDone); |
1023 | if attached_value.is_some() { |
1024 | Ok(ParseResult::AttachedValueNotConsumed) |
1025 | } else { |
1026 | Ok(ParseResult::ValuesDone) |
1027 | } |
1028 | } else { |
1029 | debug!("Requires equals but not provided. Error." ); |
1030 | Ok(ParseResult::EqualsNotProvided { |
1031 | arg: arg.to_string(), |
1032 | }) |
1033 | } |
1034 | } else if let Some(v) = attached_value { |
1035 | let arg_values = vec![v.to_owned()]; |
1036 | let trailing_idx = None; |
1037 | let react_result = ok!(self.react( |
1038 | Some(ident), |
1039 | ValueSource::CommandLine, |
1040 | arg, |
1041 | arg_values, |
1042 | trailing_idx, |
1043 | matcher, |
1044 | )); |
1045 | debug_assert_eq!(react_result, ParseResult::ValuesDone); |
1046 | // Attached are always done |
1047 | Ok(ParseResult::ValuesDone) |
1048 | } else { |
1049 | debug!("Parser::parse_opt_value: More arg vals required..." ); |
1050 | ok!(self.resolve_pending(matcher)); |
1051 | let trailing_values = false; |
1052 | matcher.pending_values_mut(arg.get_id(), Some(ident), trailing_values); |
1053 | Ok(ParseResult::Opt(arg.get_id().clone())) |
1054 | } |
1055 | } |
1056 | |
1057 | fn check_terminator(&self, arg: &Arg, val: &OsStr) -> Option<ParseResult> { |
1058 | if Some(val) == arg.terminator.as_ref().map(|s| OsStr::new(s.as_str())) { |
1059 | debug!("Parser::check_terminator: terminator={:?}" , arg.terminator); |
1060 | Some(ParseResult::ValuesDone) |
1061 | } else { |
1062 | None |
1063 | } |
1064 | } |
1065 | |
1066 | fn push_arg_values( |
1067 | &self, |
1068 | arg: &Arg, |
1069 | raw_vals: Vec<OsString>, |
1070 | source: ValueSource, |
1071 | matcher: &mut ArgMatcher, |
1072 | ) -> ClapResult<()> { |
1073 | debug!("Parser::push_arg_values: {raw_vals:?}" ); |
1074 | |
1075 | for raw_val in raw_vals { |
1076 | // update the current index because each value is a distinct index to clap |
1077 | self.cur_idx.set(self.cur_idx.get() + 1); |
1078 | debug!( |
1079 | "Parser::add_single_val_to_arg: cur_idx:={}" , |
1080 | self.cur_idx.get() |
1081 | ); |
1082 | let value_parser = arg.get_value_parser(); |
1083 | let val = ok!(value_parser.parse_ref(self.cmd, Some(arg), &raw_val, source)); |
1084 | |
1085 | matcher.add_val_to(arg.get_id(), val, raw_val); |
1086 | matcher.add_index_to(arg.get_id(), self.cur_idx.get()); |
1087 | } |
1088 | |
1089 | Ok(()) |
1090 | } |
1091 | |
1092 | fn resolve_pending(&self, matcher: &mut ArgMatcher) -> ClapResult<()> { |
1093 | let pending = match matcher.take_pending() { |
1094 | Some(pending) => pending, |
1095 | None => { |
1096 | return Ok(()); |
1097 | } |
1098 | }; |
1099 | |
1100 | debug!("Parser::resolve_pending: id={:?}" , pending.id); |
1101 | let arg = self.cmd.find(&pending.id).expect(INTERNAL_ERROR_MSG); |
1102 | let _ = ok!(self.react( |
1103 | pending.ident, |
1104 | ValueSource::CommandLine, |
1105 | arg, |
1106 | pending.raw_vals, |
1107 | pending.trailing_idx, |
1108 | matcher, |
1109 | )); |
1110 | |
1111 | Ok(()) |
1112 | } |
1113 | |
1114 | fn react( |
1115 | &self, |
1116 | ident: Option<Identifier>, |
1117 | source: ValueSource, |
1118 | arg: &Arg, |
1119 | mut raw_vals: Vec<OsString>, |
1120 | mut trailing_idx: Option<usize>, |
1121 | matcher: &mut ArgMatcher, |
1122 | ) -> ClapResult<ParseResult> { |
1123 | ok!(self.resolve_pending(matcher)); |
1124 | |
1125 | debug!( |
1126 | "Parser::react action={:?}, identifier={:?}, source={:?}" , |
1127 | arg.get_action(), |
1128 | ident, |
1129 | source |
1130 | ); |
1131 | |
1132 | // Process before `default_missing_values` to avoid it counting as values from the command |
1133 | // line |
1134 | if source == ValueSource::CommandLine { |
1135 | ok!(self.verify_num_args(arg, &raw_vals)); |
1136 | } |
1137 | |
1138 | if raw_vals.is_empty() { |
1139 | // We assume this case is valid: require equals, but min_vals == 0. |
1140 | if !arg.default_missing_vals.is_empty() { |
1141 | debug!("Parser::react: has default_missing_vals" ); |
1142 | trailing_idx = None; |
1143 | raw_vals.extend( |
1144 | arg.default_missing_vals |
1145 | .iter() |
1146 | .map(|s| s.as_os_str().to_owned()), |
1147 | ); |
1148 | } |
1149 | } |
1150 | |
1151 | if let Some(val_delim) = arg.get_value_delimiter() { |
1152 | if self.cmd.is_dont_delimit_trailing_values_set() && trailing_idx == Some(0) { |
1153 | // Nothing to do |
1154 | } else { |
1155 | let mut val_delim_buffer = [0; 4]; |
1156 | let val_delim = val_delim.encode_utf8(&mut val_delim_buffer); |
1157 | let mut split_raw_vals = Vec::with_capacity(raw_vals.len()); |
1158 | for (i, raw_val) in raw_vals.into_iter().enumerate() { |
1159 | if !raw_val.contains(val_delim) |
1160 | || (self.cmd.is_dont_delimit_trailing_values_set() |
1161 | && trailing_idx == Some(i)) |
1162 | { |
1163 | split_raw_vals.push(raw_val); |
1164 | } else { |
1165 | split_raw_vals.extend(raw_val.split(val_delim).map(|x| x.to_owned())); |
1166 | } |
1167 | } |
1168 | raw_vals = split_raw_vals |
1169 | } |
1170 | } |
1171 | |
1172 | match arg.get_action() { |
1173 | ArgAction::Set => { |
1174 | if source == ValueSource::CommandLine |
1175 | && matches!(ident, Some(Identifier::Short) | Some(Identifier::Long)) |
1176 | { |
1177 | // Record flag's index |
1178 | self.cur_idx.set(self.cur_idx.get() + 1); |
1179 | debug!("Parser::react: cur_idx:={}" , self.cur_idx.get()); |
1180 | } |
1181 | if matcher.remove(arg.get_id()) |
1182 | && !(self.cmd.is_args_override_self() || arg.overrides.contains(arg.get_id())) |
1183 | { |
1184 | return Err(ClapError::argument_conflict( |
1185 | self.cmd, |
1186 | arg.to_string(), |
1187 | vec![arg.to_string()], |
1188 | Usage::new(self.cmd).create_usage_with_title(&[]), |
1189 | )); |
1190 | } |
1191 | self.start_custom_arg(matcher, arg, source); |
1192 | ok!(self.push_arg_values(arg, raw_vals, source, matcher)); |
1193 | if cfg!(debug_assertions) && matcher.needs_more_vals(arg) { |
1194 | debug!( |
1195 | "Parser::react not enough values passed in, leaving it to the validator to complain" , |
1196 | ); |
1197 | } |
1198 | Ok(ParseResult::ValuesDone) |
1199 | } |
1200 | ArgAction::Append => { |
1201 | if source == ValueSource::CommandLine |
1202 | && matches!(ident, Some(Identifier::Short) | Some(Identifier::Long)) |
1203 | { |
1204 | // Record flag's index |
1205 | self.cur_idx.set(self.cur_idx.get() + 1); |
1206 | debug!("Parser::react: cur_idx:={}" , self.cur_idx.get()); |
1207 | } |
1208 | self.start_custom_arg(matcher, arg, source); |
1209 | ok!(self.push_arg_values(arg, raw_vals, source, matcher)); |
1210 | if cfg!(debug_assertions) && matcher.needs_more_vals(arg) { |
1211 | debug!( |
1212 | "Parser::react not enough values passed in, leaving it to the validator to complain" , |
1213 | ); |
1214 | } |
1215 | Ok(ParseResult::ValuesDone) |
1216 | } |
1217 | ArgAction::SetTrue => { |
1218 | let raw_vals = if raw_vals.is_empty() { |
1219 | vec![OsString::from("true" )] |
1220 | } else { |
1221 | raw_vals |
1222 | }; |
1223 | |
1224 | if matcher.remove(arg.get_id()) |
1225 | && !(self.cmd.is_args_override_self() || arg.overrides.contains(arg.get_id())) |
1226 | { |
1227 | return Err(ClapError::argument_conflict( |
1228 | self.cmd, |
1229 | arg.to_string(), |
1230 | vec![arg.to_string()], |
1231 | Usage::new(self.cmd).create_usage_with_title(&[]), |
1232 | )); |
1233 | } |
1234 | self.start_custom_arg(matcher, arg, source); |
1235 | ok!(self.push_arg_values(arg, raw_vals, source, matcher)); |
1236 | Ok(ParseResult::ValuesDone) |
1237 | } |
1238 | ArgAction::SetFalse => { |
1239 | let raw_vals = if raw_vals.is_empty() { |
1240 | vec![OsString::from("false" )] |
1241 | } else { |
1242 | raw_vals |
1243 | }; |
1244 | |
1245 | if matcher.remove(arg.get_id()) |
1246 | && !(self.cmd.is_args_override_self() || arg.overrides.contains(arg.get_id())) |
1247 | { |
1248 | return Err(ClapError::argument_conflict( |
1249 | self.cmd, |
1250 | arg.to_string(), |
1251 | vec![arg.to_string()], |
1252 | Usage::new(self.cmd).create_usage_with_title(&[]), |
1253 | )); |
1254 | } |
1255 | self.start_custom_arg(matcher, arg, source); |
1256 | ok!(self.push_arg_values(arg, raw_vals, source, matcher)); |
1257 | Ok(ParseResult::ValuesDone) |
1258 | } |
1259 | ArgAction::Count => { |
1260 | let raw_vals = if raw_vals.is_empty() { |
1261 | let existing_value = *matcher |
1262 | .get_one::<crate::builder::CountType>(arg.get_id().as_str()) |
1263 | .unwrap_or(&0); |
1264 | let next_value = existing_value.saturating_add(1); |
1265 | vec![OsString::from(next_value.to_string())] |
1266 | } else { |
1267 | raw_vals |
1268 | }; |
1269 | |
1270 | matcher.remove(arg.get_id()); |
1271 | self.start_custom_arg(matcher, arg, source); |
1272 | ok!(self.push_arg_values(arg, raw_vals, source, matcher)); |
1273 | Ok(ParseResult::ValuesDone) |
1274 | } |
1275 | ArgAction::Help => { |
1276 | let use_long = match ident { |
1277 | Some(Identifier::Long) => true, |
1278 | Some(Identifier::Short) => false, |
1279 | Some(Identifier::Index) => true, |
1280 | None => true, |
1281 | }; |
1282 | debug!("Help: use_long={use_long}" ); |
1283 | Err(self.help_err(use_long)) |
1284 | } |
1285 | ArgAction::HelpShort => { |
1286 | let use_long = false; |
1287 | debug!("Help: use_long={use_long}" ); |
1288 | Err(self.help_err(use_long)) |
1289 | } |
1290 | ArgAction::HelpLong => { |
1291 | let use_long = true; |
1292 | debug!("Help: use_long={use_long}" ); |
1293 | Err(self.help_err(use_long)) |
1294 | } |
1295 | ArgAction::Version => { |
1296 | let use_long = match ident { |
1297 | Some(Identifier::Long) => true, |
1298 | Some(Identifier::Short) => false, |
1299 | Some(Identifier::Index) => true, |
1300 | None => true, |
1301 | }; |
1302 | debug!("Version: use_long={use_long}" ); |
1303 | Err(self.version_err(use_long)) |
1304 | } |
1305 | } |
1306 | } |
1307 | |
1308 | fn verify_num_args(&self, arg: &Arg, raw_vals: &[OsString]) -> ClapResult<()> { |
1309 | if self.cmd.is_ignore_errors_set() { |
1310 | return Ok(()); |
1311 | } |
1312 | |
1313 | let actual = raw_vals.len(); |
1314 | let expected = arg.get_num_args().expect(INTERNAL_ERROR_MSG); |
1315 | |
1316 | if 0 < expected.min_values() && actual == 0 { |
1317 | // Issue 665 (https://github.com/clap-rs/clap/issues/665) |
1318 | // Issue 1105 (https://github.com/clap-rs/clap/issues/1105) |
1319 | return Err(ClapError::empty_value( |
1320 | self.cmd, |
1321 | &super::get_possible_values_cli(arg) |
1322 | .iter() |
1323 | .filter(|pv| !pv.is_hide_set()) |
1324 | .map(|n| n.get_name().to_owned()) |
1325 | .collect::<Vec<_>>(), |
1326 | arg.to_string(), |
1327 | )); |
1328 | } else if let Some(expected) = expected.num_values() { |
1329 | if expected != actual { |
1330 | debug!("Validator::validate_arg_num_vals: Sending error WrongNumberOfValues" ); |
1331 | return Err(ClapError::wrong_number_of_values( |
1332 | self.cmd, |
1333 | arg.to_string(), |
1334 | expected, |
1335 | actual, |
1336 | Usage::new(self.cmd).create_usage_with_title(&[]), |
1337 | )); |
1338 | } |
1339 | } else if actual < expected.min_values() { |
1340 | return Err(ClapError::too_few_values( |
1341 | self.cmd, |
1342 | arg.to_string(), |
1343 | expected.min_values(), |
1344 | actual, |
1345 | Usage::new(self.cmd).create_usage_with_title(&[]), |
1346 | )); |
1347 | } else if expected.max_values() < actual { |
1348 | debug!("Validator::validate_arg_num_vals: Sending error TooManyValues" ); |
1349 | return Err(ClapError::too_many_values( |
1350 | self.cmd, |
1351 | raw_vals |
1352 | .last() |
1353 | .expect(INTERNAL_ERROR_MSG) |
1354 | .to_string_lossy() |
1355 | .into_owned(), |
1356 | arg.to_string(), |
1357 | Usage::new(self.cmd).create_usage_with_title(&[]), |
1358 | )); |
1359 | } |
1360 | |
1361 | Ok(()) |
1362 | } |
1363 | |
1364 | fn remove_overrides(&self, arg: &Arg, matcher: &mut ArgMatcher) { |
1365 | debug!("Parser::remove_overrides: id={:?}" , arg.id); |
1366 | for override_id in &arg.overrides { |
1367 | debug!("Parser::remove_overrides:iter:{override_id:?}: removing" ); |
1368 | matcher.remove(override_id); |
1369 | } |
1370 | |
1371 | // Override anything that can override us |
1372 | let mut transitive = Vec::new(); |
1373 | for arg_id in matcher.arg_ids() { |
1374 | if let Some(overrider) = self.cmd.find(arg_id) { |
1375 | if overrider.overrides.contains(arg.get_id()) { |
1376 | transitive.push(overrider.get_id()); |
1377 | } |
1378 | } |
1379 | } |
1380 | for overrider_id in transitive { |
1381 | debug!("Parser::remove_overrides:iter:{overrider_id:?}: removing" ); |
1382 | matcher.remove(overrider_id); |
1383 | } |
1384 | } |
1385 | |
1386 | #[cfg (feature = "env" )] |
1387 | fn add_env(&mut self, matcher: &mut ArgMatcher) -> ClapResult<()> { |
1388 | debug!("Parser::add_env" ); |
1389 | |
1390 | for arg in self.cmd.get_arguments() { |
1391 | // Use env only if the arg was absent among command line args, |
1392 | // early return if this is not the case. |
1393 | if matcher.contains(&arg.id) { |
1394 | debug!("Parser::add_env: Skipping existing arg `{arg}`" ); |
1395 | continue; |
1396 | } |
1397 | |
1398 | debug!("Parser::add_env: Checking arg `{arg}`" ); |
1399 | if let Some((_, Some(ref val))) = arg.env { |
1400 | debug!("Parser::add_env: Found an opt with value={val:?}" ); |
1401 | let arg_values = vec![val.to_owned()]; |
1402 | let trailing_idx = None; |
1403 | let _ = ok!(self.react( |
1404 | None, |
1405 | ValueSource::EnvVariable, |
1406 | arg, |
1407 | arg_values, |
1408 | trailing_idx, |
1409 | matcher, |
1410 | )); |
1411 | } |
1412 | } |
1413 | |
1414 | Ok(()) |
1415 | } |
1416 | |
1417 | fn add_defaults(&self, matcher: &mut ArgMatcher) -> ClapResult<()> { |
1418 | debug!("Parser::add_defaults" ); |
1419 | |
1420 | for arg in self.cmd.get_arguments() { |
1421 | debug!("Parser::add_defaults:iter:{}:" , arg.get_id()); |
1422 | ok!(self.add_default_value(arg, matcher)); |
1423 | } |
1424 | |
1425 | Ok(()) |
1426 | } |
1427 | |
1428 | fn add_default_value(&self, arg: &Arg, matcher: &mut ArgMatcher) -> ClapResult<()> { |
1429 | if !arg.default_vals_ifs.is_empty() { |
1430 | debug!("Parser::add_default_value: has conditional defaults" ); |
1431 | if !matcher.contains(arg.get_id()) { |
1432 | for (id, val, default) in arg.default_vals_ifs.iter() { |
1433 | let add = if let Some(a) = matcher.get(id) { |
1434 | match val { |
1435 | crate::builder::ArgPredicate::Equals(v) => { |
1436 | a.raw_vals_flatten().any(|value| v == value) |
1437 | } |
1438 | crate::builder::ArgPredicate::IsPresent => true, |
1439 | } |
1440 | } else { |
1441 | false |
1442 | }; |
1443 | |
1444 | if add { |
1445 | if let Some(default) = default { |
1446 | let arg_values = vec![default.to_os_string()]; |
1447 | let trailing_idx = None; |
1448 | let _ = ok!(self.react( |
1449 | None, |
1450 | ValueSource::DefaultValue, |
1451 | arg, |
1452 | arg_values, |
1453 | trailing_idx, |
1454 | matcher, |
1455 | )); |
1456 | } |
1457 | return Ok(()); |
1458 | } |
1459 | } |
1460 | } |
1461 | } else { |
1462 | debug!("Parser::add_default_value: doesn't have conditional defaults" ); |
1463 | } |
1464 | |
1465 | if !arg.default_vals.is_empty() { |
1466 | debug!( |
1467 | "Parser::add_default_value:iter:{}: has default vals" , |
1468 | arg.get_id() |
1469 | ); |
1470 | if matcher.contains(arg.get_id()) { |
1471 | debug!("Parser::add_default_value:iter:{}: was used" , arg.get_id()); |
1472 | // do nothing |
1473 | } else { |
1474 | debug!( |
1475 | "Parser::add_default_value:iter:{}: wasn't used" , |
1476 | arg.get_id() |
1477 | ); |
1478 | let arg_values: Vec<_> = arg |
1479 | .default_vals |
1480 | .iter() |
1481 | .map(crate::builder::OsStr::to_os_string) |
1482 | .collect(); |
1483 | let trailing_idx = None; |
1484 | let _ = ok!(self.react( |
1485 | None, |
1486 | ValueSource::DefaultValue, |
1487 | arg, |
1488 | arg_values, |
1489 | trailing_idx, |
1490 | matcher, |
1491 | )); |
1492 | } |
1493 | } else { |
1494 | debug!( |
1495 | "Parser::add_default_value:iter:{}: doesn't have default vals" , |
1496 | arg.get_id() |
1497 | ); |
1498 | |
1499 | // do nothing |
1500 | } |
1501 | |
1502 | Ok(()) |
1503 | } |
1504 | |
1505 | fn start_custom_arg(&self, matcher: &mut ArgMatcher, arg: &Arg, source: ValueSource) { |
1506 | if source == ValueSource::CommandLine { |
1507 | // With each new occurrence, remove overrides from prior occurrences |
1508 | self.remove_overrides(arg, matcher); |
1509 | } |
1510 | matcher.start_custom_arg(arg, source); |
1511 | if source.is_explicit() { |
1512 | for group in self.cmd.groups_for_arg(arg.get_id()) { |
1513 | matcher.start_custom_group(group.clone(), source); |
1514 | matcher.add_val_to( |
1515 | &group, |
1516 | AnyValue::new(arg.get_id().clone()), |
1517 | OsString::from(arg.get_id().as_str()), |
1518 | ); |
1519 | } |
1520 | } |
1521 | } |
1522 | } |
1523 | |
1524 | // Error, Help, and Version Methods |
1525 | impl<'cmd> Parser<'cmd> { |
1526 | /// Is only used for the long flag(which is the only one needs fuzzy searching) |
1527 | fn did_you_mean_error( |
1528 | &mut self, |
1529 | arg: &str, |
1530 | matcher: &mut ArgMatcher, |
1531 | remaining_args: &[&OsStr], |
1532 | trailing_values: bool, |
1533 | ) -> ClapError { |
1534 | debug!("Parser::did_you_mean_error: arg={arg}" ); |
1535 | // Didn't match a flag or option |
1536 | let longs = self |
1537 | .cmd |
1538 | .get_keymap() |
1539 | .keys() |
1540 | .filter_map(|x| match x { |
1541 | KeyType::Long(l) => Some(l.to_string_lossy().into_owned()), |
1542 | _ => None, |
1543 | }) |
1544 | .collect::<Vec<_>>(); |
1545 | debug!("Parser::did_you_mean_error: longs={longs:?}" ); |
1546 | |
1547 | let did_you_mean = suggestions::did_you_mean_flag( |
1548 | arg, |
1549 | remaining_args, |
1550 | longs.iter().map(|x| &x[..]), |
1551 | self.cmd.get_subcommands_mut(), |
1552 | ); |
1553 | |
1554 | // Add the arg to the matches to build a proper usage string |
1555 | if let Some((name, _)) = did_you_mean.as_ref() { |
1556 | if let Some(arg) = self.cmd.get_keymap().get(&name.as_ref()) { |
1557 | self.start_custom_arg(matcher, arg, ValueSource::CommandLine); |
1558 | } |
1559 | } |
1560 | let did_you_mean = did_you_mean.map(|(arg, cmd)| (format!("-- {arg}" ), cmd)); |
1561 | |
1562 | let required = self.cmd.required_graph(); |
1563 | let used: Vec<Id> = matcher |
1564 | .arg_ids() |
1565 | .filter(|arg_id| { |
1566 | matcher.check_explicit(arg_id, &crate::builder::ArgPredicate::IsPresent) |
1567 | }) |
1568 | .filter(|n| self.cmd.find(n).map(|a| !a.is_hide_set()).unwrap_or(true)) |
1569 | .cloned() |
1570 | .collect(); |
1571 | |
1572 | // `did_you_mean` is a lot more likely and should cause us to skip the `--` suggestion |
1573 | // |
1574 | // In theory, this is only called for `--long`s, so we don't need to check |
1575 | let suggested_trailing_arg = |
1576 | did_you_mean.is_none() && !trailing_values && self.cmd.has_positionals(); |
1577 | ClapError::unknown_argument( |
1578 | self.cmd, |
1579 | format!("-- {arg}" ), |
1580 | did_you_mean, |
1581 | suggested_trailing_arg, |
1582 | Usage::new(self.cmd) |
1583 | .required(&required) |
1584 | .create_usage_with_title(&used), |
1585 | ) |
1586 | } |
1587 | |
1588 | fn help_err(&self, use_long: bool) -> ClapError { |
1589 | let styled = self.cmd.write_help_err(use_long); |
1590 | ClapError::display_help(self.cmd, styled) |
1591 | } |
1592 | |
1593 | fn version_err(&self, use_long: bool) -> ClapError { |
1594 | let styled = self.cmd.write_version_err(use_long); |
1595 | ClapError::display_version(self.cmd, styled) |
1596 | } |
1597 | } |
1598 | |
1599 | #[derive (Debug, PartialEq, Eq)] |
1600 | pub(crate) enum ParseState { |
1601 | ValuesDone, |
1602 | Opt(Id), |
1603 | Pos(Id), |
1604 | } |
1605 | |
1606 | /// Recoverable Parsing results. |
1607 | #[derive (Debug, PartialEq, Clone)] |
1608 | #[must_use ] |
1609 | enum ParseResult { |
1610 | FlagSubCommand(String), |
1611 | Opt(Id), |
1612 | ValuesDone, |
1613 | /// Value attached to the short flag is not consumed(e.g. 'u' for `-cu` is |
1614 | /// not consumed). |
1615 | AttachedValueNotConsumed, |
1616 | /// This long flag doesn't need a value but is provided one. |
1617 | UnneededAttachedValue { |
1618 | rest: String, |
1619 | used: Vec<Id>, |
1620 | arg: String, |
1621 | }, |
1622 | /// This flag might be an hyphen Value. |
1623 | MaybeHyphenValue, |
1624 | /// Equals required but not provided. |
1625 | EqualsNotProvided { |
1626 | arg: String, |
1627 | }, |
1628 | /// Failed to match a Arg. |
1629 | NoMatchingArg { |
1630 | arg: String, |
1631 | }, |
1632 | /// No argument found e.g. parser is given `-` when parsing a flag. |
1633 | NoArg, |
1634 | } |
1635 | |
1636 | #[derive (Clone, Debug, PartialEq, Eq)] |
1637 | pub(crate) struct PendingArg { |
1638 | pub(crate) id: Id, |
1639 | pub(crate) ident: Option<Identifier>, |
1640 | pub(crate) raw_vals: Vec<OsString>, |
1641 | pub(crate) trailing_idx: Option<usize>, |
1642 | } |
1643 | |
1644 | #[derive (Copy, Clone, Debug, PartialEq, Eq)] |
1645 | pub(crate) enum Identifier { |
1646 | Short, |
1647 | Long, |
1648 | Index, |
1649 | } |
1650 | |