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