1use std::ffi::OsString;
2
3pub(crate) use crate::arg::*;
4use crate::{
5 error::{Message, MissingItem},
6 item::Item,
7 meta_help::Metavar,
8 parsers::NamedArg,
9 Error,
10};
11
12pub struct Args<'a> {
36 items: Box<dyn ExactSizeIterator<Item = OsString> + 'a>,
37 name: Option<String>,
38 #[cfg(feature = "autocomplete")]
39 c_rev: Option<usize>,
40}
41
42impl Args<'_> {
43 #[cfg(feature = "autocomplete")]
64 #[must_use]
65 pub fn set_comp(mut self, rev: usize) -> Self {
66 self.c_rev = Some(rev);
67 self
68 }
69
70 #[must_use]
81 pub fn set_name(mut self, name: &str) -> Self {
82 self.name = Some(name.to_owned());
83 self
84 }
85}
86
87impl<const N: usize> From<&'static [&'static str; N]> for Args<'_> {
88 fn from(value: &'static [&'static str; N]) -> Self {
89 Self {
90 items: Box::new(value.iter().map(OsString::from)),
91 #[cfg(feature = "autocomplete")]
92 c_rev: None,
93 name: None,
94 }
95 }
96}
97
98impl<'a> From<&'a [&'a std::ffi::OsStr]> for Args<'a> {
99 fn from(value: &'a [&'a std::ffi::OsStr]) -> Self {
100 Self {
101 items: Box::new(value.iter().map(OsString::from)),
102 #[cfg(feature = "autocomplete")]
103 c_rev: None,
104 name: None,
105 }
106 }
107}
108
109impl<'a> From<&'a [&'a str]> for Args<'a> {
110 fn from(value: &'a [&'a str]) -> Self {
111 Self {
112 items: Box::new(value.iter().map(OsString::from)),
113 #[cfg(feature = "autocomplete")]
114 c_rev: None,
115 name: None,
116 }
117 }
118}
119
120impl<'a> From<&'a [String]> for Args<'a> {
121 fn from(value: &'a [String]) -> Self {
122 Self {
123 items: Box::new(value.iter().map(OsString::from)),
124 #[cfg(feature = "autocomplete")]
125 c_rev: None,
126 name: None,
127 }
128 }
129}
130
131impl<'a> From<&'a [OsString]> for Args<'a> {
132 fn from(value: &'a [OsString]) -> Self {
133 Self {
134 items: Box::new(value.iter().map(OsString::from)),
135 #[cfg(feature = "autocomplete")]
136 c_rev: None,
137 name: None,
138 }
139 }
140}
141
142impl Args<'_> {
143 #[must_use]
145 pub fn current_args() -> Self {
146 let mut value = std::env::args_os();
147 let name = value.next().and_then(|n| {
148 let path = std::path::PathBuf::from(n);
149 let file_name = path.file_name()?;
150 let s = file_name.to_str()?;
151 Some(s.to_owned())
152 });
153 Self {
154 items: Box::new(value),
155 #[cfg(feature = "autocomplete")]
156 c_rev: None,
157 name,
158 }
159 }
160}
161
162#[derive(Debug, Clone, Copy, Eq, PartialEq)]
164pub(crate) enum ItemState {
165 Unparsed,
167 Conflict(usize),
169 Parsed,
171}
172
173impl ItemState {
174 pub(crate) fn parsed(&self) -> bool {
175 match self {
176 ItemState::Unparsed | ItemState::Conflict(_) => false,
177 ItemState::Parsed => true,
178 }
179 }
180 pub(crate) fn present(&self) -> bool {
181 match self {
182 ItemState::Unparsed | ItemState::Conflict(_) => true,
183 ItemState::Parsed => false,
184 }
185 }
186}
187
188fn disambiguate_short(
189 mut os: OsString,
190 short: String,
191 short_flags: &[char],
192 short_args: &[char],
193 items: &mut Vec<Arg>,
194) -> Option<Message> {
195 let original = items.len();
201
202 let mut first_flag = os.clone();
204
205 for (ix, c) in short.char_indices() {
206 let tail_ix = ix + c.len_utf8();
207 let rest = &short[tail_ix..];
208
209 if ix == 0 && rest.is_empty() {
211 items.push(Arg::Short(c, false, std::mem::take(&mut first_flag)));
212 return None;
213 }
214 match (short_flags.contains(&c), short_args.contains(&c)) {
215 (true, false) => {
217 items.push(Arg::Short(c, false, std::mem::take(&mut first_flag)));
218 }
219
220 (false, true) => {
222 let adjacent_body = !rest.is_empty();
223 items.push(Arg::Short(c, adjacent_body, std::mem::take(&mut os)));
224 if adjacent_body {
225 items.push(Arg::Word(rest.into()));
226 }
227 return None;
228 }
229
230 (false, false) => {
232 items.truncate(original);
233 items.push(Arg::Word(os));
234 return None;
235 }
236
237 (true, true) => {
239 let msg = Message::Ambiguity(items.len(), short);
240 items.push(Arg::Word(std::mem::take(&mut os)));
241 return Some(msg);
242 }
243 }
244 }
245 None
246}
247
248pub use inner::State;
249mod inner {
251 use std::{ops::Range, rc::Rc};
252
253 use crate::{error::Message, item::Item, Args};
254
255 use super::{split_os_argument, Arg, ArgType, ItemState};
256 #[derive(Clone, Debug)]
257 #[doc(hidden)]
258 pub struct State {
259 pub(crate) items: Rc<[Arg]>,
261
262 item_state: Vec<ItemState>,
263
264 remaining: usize,
266
267 #[doc(hidden)]
268 pub current: Option<usize>,
272
273 pub(crate) path: Vec<String>,
275
276 #[cfg(feature = "autocomplete")]
277 comp: Option<crate::complete_gen::Complete>,
278
279 scope: Range<usize>,
286 }
287
288 impl State {
289 pub(crate) fn present(&self, ix: usize) -> Option<bool> {
291 Some(self.item_state.get(ix)?.present())
292 }
293
294 pub(crate) fn depth(&self) -> usize {
295 self.path.len()
296 }
297 }
298
299 pub(crate) struct ArgsIter<'a> {
300 args: &'a State,
301 cur: usize,
302 }
303
304 impl State {
305 #[cfg(feature = "autocomplete")]
306 pub(crate) fn check_no_pos_ahead(&self) -> bool {
307 self.comp.as_ref().map_or(false, |c| c.no_pos_ahead)
308 }
309
310 #[cfg(feature = "autocomplete")]
311 pub(crate) fn set_no_pos_ahead(&mut self) {
312 if let Some(comp) = &mut self.comp {
313 comp.no_pos_ahead = true;
314 }
315 }
316
317 #[allow(clippy::too_many_lines)] pub(crate) fn construct(
319 args: Args,
320 short_flags: &[char],
321 short_args: &[char],
322 err: &mut Option<Message>,
323 ) -> State {
324 let mut items = Vec::new();
325 let mut pos_only = false;
326 let mut double_dash_marker = None;
327
328 #[cfg(feature = "autocomplete")]
329 let mut comp_scanner = crate::complete_run::ArgScanner {
330 revision: args.c_rev,
331 name: args.name.as_deref(),
332 };
333
334 for os in args.items {
335 if pos_only {
336 items.push(Arg::PosWord(os));
337 continue;
338 }
339
340 #[cfg(feature = "autocomplete")]
341 if comp_scanner.check_next(&os) {
342 continue;
343 }
344
345 match split_os_argument(&os) {
346 Some((ArgType::Short, short, None)) => {
348 if let Some(msg) = super::disambiguate_short(
349 os,
350 short,
351 short_flags,
352 short_args,
353 &mut items,
354 ) {
355 *err = Some(msg);
356 break;
357 }
358 }
359 Some((ArgType::Short, short, Some(arg))) => {
360 let mut chars = short.chars();
361 items.push(Arg::Short(chars.next().unwrap(), true, os));
362 items.push(arg);
363 }
364 Some((ArgType::Long, long, arg)) => {
366 items.push(Arg::Long(long, arg.is_some(), os));
367 if let Some(arg) = arg {
368 items.push(arg);
369 }
370 }
371 None => {
374 if os == "--" {
375 double_dash_marker = Some(items.len());
376 pos_only = true;
377 }
378 items.push(if pos_only {
379 Arg::PosWord(os)
380 } else {
381 Arg::Word(os)
382 });
383 }
384 }
385 }
386
387 let mut item_state = vec![ItemState::Unparsed; items.len()];
388 let mut remaining = items.len();
389 if let Some(ix) = double_dash_marker {
390 item_state[ix] = ItemState::Parsed;
391 remaining -= 1;
392
393 #[cfg(feature = "autocomplete")]
394 if comp_scanner.revision.is_some() && ix == items.len() - 1 {
395 remaining += 1;
396 item_state[ix] = ItemState::Unparsed;
397 }
398 }
399
400 let mut path = Vec::new();
401
402 #[cfg(feature = "autocomplete")]
403 let comp = comp_scanner.done();
404
405 if let Some(name) = args.name {
406 path.push(name);
407 }
408 State {
409 item_state,
410 remaining,
411 scope: 0..items.len(),
412 items: items.into(),
413 current: None,
414 path,
415 #[cfg(feature = "autocomplete")]
416 comp,
417 }
418 }
419 }
420
421 impl<'a> State {
422 pub(crate) fn items_iter(&'a self) -> ArgsIter<'a> {
424 ArgsIter {
425 args: self,
426 cur: self.scope.start,
427 }
428 }
429
430 pub(crate) fn remove(&mut self, index: usize) {
431 if self.scope.contains(&index) && self.item_state[index].present() {
432 self.current = Some(index);
433 self.remaining -= 1;
434 self.item_state[index] = ItemState::Parsed;
435 }
436 }
437
438 pub(crate) fn pick_winner(&self, other: &Self) -> (bool, Option<usize>) {
439 for (ix, (me, other)) in self
440 .item_state
441 .iter()
442 .zip(other.item_state.iter())
443 .enumerate()
444 {
445 if me.parsed() ^ other.parsed() {
446 return (me.parsed(), Some(ix));
447 }
448 }
449 (true, None)
450 }
451
452 pub(crate) fn conflict(&self) -> Option<(usize, usize)> {
454 let (ix, _item) = self.items_iter().next()?;
455 if let ItemState::Conflict(other) = self.item_state.get(ix)? {
456 Some((ix, *other))
457 } else {
458 None
459 }
460 }
461
462 pub(crate) fn save_conflicts(&mut self, loser: &State, win: usize) {
463 for (winner, loser) in self.item_state.iter_mut().zip(loser.item_state.iter()) {
464 if winner.present() && loser.parsed() {
465 *winner = ItemState::Conflict(win);
466 }
467 }
468 }
469
470 #[allow(dead_code)]
471 pub(crate) fn is_empty(&self) -> bool {
473 self.remaining == 0
474 }
475
476 pub(crate) fn len(&self) -> usize {
477 self.remaining
478 }
479
480 pub(crate) fn get(&self, ix: usize) -> Option<&Arg> {
482 if self.scope.contains(&ix) && self.item_state.get(ix)?.present() {
483 Some(self.items.get(ix)?)
484 } else {
485 None
486 }
487 }
488
489 #[cfg(feature = "autocomplete")]
490 #[must_use]
494 pub fn is_comp(&self) -> bool {
495 self.comp.is_some()
496 }
497
498 pub(crate) fn adjacent_scope(&self, original: &State) -> Option<Range<usize>> {
500 if self.items.is_empty() {
501 return None;
502 }
503
504 let start = self.scope().start;
506 for (mut offset, (this, orig)) in self.item_state[start..]
507 .iter()
508 .zip(original.item_state[start..].iter())
509 .enumerate()
510 {
511 offset += start;
512 if this.present() && orig.present() {
516 let proposed_scope = start..offset;
517 return if self.scope() == proposed_scope {
518 None
519 } else {
520 Some(proposed_scope)
521 };
522 }
523 }
524 None
525 }
526
527 pub(crate) fn adjacently_available_from(&self, start: usize) -> Range<usize> {
529 let span_size = self
530 .item_state
531 .iter()
532 .copied()
533 .skip(start)
534 .take_while(ItemState::present)
535 .count();
536 start..start + span_size
537 }
538
539 pub(crate) fn ranges(&'a self, item: &'a Item) -> ArgRangesIter<'a> {
540 let width = match item {
541 Item::Any { .. }
542 | Item::Positional { .. }
543 | Item::Command { .. }
544 | Item::Flag { .. } => 1,
545 Item::Argument { .. } => 2,
546 };
547 ArgRangesIter {
548 args: self,
549 cur: 0,
550 width,
551 }
552 }
553
554 pub(crate) fn scope(&self) -> Range<usize> {
555 self.scope.clone()
556 }
557
558 pub(crate) fn set_scope(&mut self, scope: Range<usize>) {
560 self.scope = scope;
561 self.remaining = self.item_state[self.scope()]
562 .iter()
563 .copied()
564 .filter(ItemState::present)
565 .count();
566 }
567
568 #[cfg(feature = "autocomplete")]
569 pub(crate) fn touching_last_remove(&self) -> bool {
571 self.comp.is_some() && self.items.len() - 1 == self.current.unwrap_or(usize::MAX)
572 }
573
574 #[cfg(feature = "autocomplete")]
575 pub(crate) fn comp_mut(&mut self) -> Option<&mut crate::complete_gen::Complete> {
576 self.comp.as_mut()
577 }
578
579 #[cfg(feature = "autocomplete")]
580 pub(crate) fn comp_ref(&self) -> Option<&crate::complete_gen::Complete> {
581 self.comp.as_ref()
582 }
583
584 #[cfg(feature = "autocomplete")]
585 pub(crate) fn swap_comps(&mut self, other: &mut Self) {
586 std::mem::swap(&mut self.comp, &mut other.comp);
587 }
588 }
589
590 pub(crate) struct ArgRangesIter<'a> {
591 args: &'a State,
592 width: usize,
593 cur: usize,
594 }
595 impl<'a> Iterator for ArgRangesIter<'a> {
596 type Item = (
597 usize,
598 usize,
599 State,
600 );
601
602 fn next(&mut self) -> Option<Self::Item> {
603 loop {
604 let cur = self.cur;
605 if cur > self.args.scope.end {
606 return None;
607 }
608 self.cur += 1;
609 if !self.args.present(cur)? {
610 continue;
611 }
612 if cur + self.width > self.args.items.len() {
613 return None;
614 }
615 let mut args = self.args.clone();
618 args.set_scope(cur..self.args.items.len());
619 return Some((cur, self.width, args));
620 }
621 }
622 }
623
624 impl<'a> Iterator for ArgsIter<'a> {
625 type Item = (usize, &'a Arg);
626
627 fn next(&mut self) -> Option<Self::Item> {
628 loop {
629 let ix = self.cur;
630 if !self.args.scope.contains(&ix) {
631 return None;
632 }
633 self.cur += 1;
634 if self.args.item_state.get(ix)?.present() {
635 return Some((ix, &self.args.items[ix]));
636 }
637 }
638 }
639 }
640}
641
642impl State {
643 #[inline(never)]
644 #[cfg(feature = "autocomplete")]
645 pub(crate) fn swap_comps_with(&mut self, comps: &mut Vec<crate::complete_gen::Comp>) {
646 if let Some(comp) = self.comp_mut() {
647 comp.swap_comps(comps);
648 }
649 }
650
651 pub(crate) fn take_flag(&mut self, named: &NamedArg) -> bool {
655 if let Some((ix, _)) = self
656 .items_iter()
657 .find(|arg| named.matches_arg(arg.1, false))
658 {
659 self.remove(ix);
660 true
661 } else {
662 false
663 }
664 }
665
666 pub(crate) fn take_arg(
671 &mut self,
672 named: &NamedArg,
673 adjacent: bool,
674 metavar: Metavar,
675 ) -> Result<Option<OsString>, Error> {
676 let (key_ix, _arg) = match self
677 .items_iter()
678 .find(|arg| named.matches_arg(arg.1, adjacent))
679 {
680 Some(v) => v,
681 None => return Ok(None),
682 };
683
684 let val_ix = key_ix + 1;
685 let val = match self.get(val_ix) {
686 Some(Arg::Word(w) | Arg::ArgWord(w)) => w,
687 _ => return Err(Error(Message::NoArgument(key_ix, metavar))),
688 };
689 let val = val.clone();
690 self.current = Some(val_ix);
691 self.remove(key_ix);
692 self.remove(val_ix);
693 Ok(Some(val))
694 }
695
696 pub(crate) fn take_positional_word(
701 &mut self,
702 metavar: Metavar,
703 ) -> Result<(usize, bool, OsString), Error> {
704 match self.items_iter().find_map(|(ix, arg)| match arg {
705 Arg::Word(w) => Some((ix, false, w)),
706 Arg::PosWord(w) => Some((ix, true, w)),
707 _ => None,
708 }) {
709 Some((ix, strict, w)) => {
710 let w = w.clone();
711 self.current = Some(ix);
712 self.remove(ix);
713 Ok((ix, strict, w))
714 }
715 None => {
716 let scope = self.scope();
717 let missing = MissingItem {
718 item: Item::Positional {
719 help: None,
720 metavar,
721 },
722 position: scope.start,
723 scope,
724 };
725 Err(Error(Message::Missing(vec![missing])))
726 }
727 }
728 }
729
730 pub(crate) fn take_cmd(&mut self, word: &str) -> bool {
732 if let Some((ix, Arg::Word(w) | Arg::Short(_, _, w) | Arg::Long(_, false, w))) =
733 self.items_iter().next()
734 {
735 if w == word {
736 self.remove(ix);
737 self.current = Some(ix);
738 return true;
739 }
740 }
741 self.current = None;
742 false
743 }
744
745 #[cfg(test)]
746 pub(crate) fn peek(&self) -> Option<&Arg> {
747 self.items_iter().next().map(|x| x.1)
748 }
749}
750
751#[cfg(test)]
752mod tests {
753 use super::*;
754 use crate::meta_help::Metavar;
755 use crate::{long, short};
756 const M: Metavar = Metavar("M");
757
758 #[allow(clippy::fallible_impl_from)] impl<const N: usize> From<&'static [&'static str; N]> for State {
760 fn from(value: &'static [&'static str; N]) -> Self {
761 let args = Args::from(value);
762 let mut msg = None;
763 let res = State::construct(args, &[], &[], &mut msg);
764 if let Some(err) = &msg {
765 panic!("Couldn't construct state: {:?}/{:?}", err, res);
766 }
767 res
768 }
769 }
770
771 #[test]
772 fn long_arg() {
773 let mut a = State::from(&["--speed", "12"]);
774 let s = a.take_arg(&long("speed"), false, M).unwrap().unwrap();
775 assert_eq!(s, "12");
776 assert!(a.is_empty());
777 }
778 #[test]
779 fn long_flag_and_positional() {
780 let mut a = State::from(&["--speed", "12"]);
781 let flag = a.take_flag(&long("speed"));
782 assert!(flag);
783 assert!(!a.is_empty());
784 let s = a.take_positional_word(M).unwrap();
785 assert_eq!(s.2, "12");
786 assert!(a.is_empty());
787 }
788
789 #[test]
790 fn multiple_short_flags() {
791 let args = Args::from(&["-vvv"]);
792 let mut err = None;
793 let mut a = State::construct(args, &['v'], &[], &mut err);
794 assert!(a.take_flag(&short('v')));
795 assert!(a.take_flag(&short('v')));
796 assert!(a.take_flag(&short('v')));
797 assert!(!a.take_flag(&short('v')));
798 assert!(a.is_empty());
799 }
800
801 #[test]
802 fn long_arg_with_equality() {
803 let mut a = State::from(&["--speed=12"]);
804 let s = a.take_arg(&long("speed"), false, M).unwrap().unwrap();
805 assert_eq!(s, "12");
806 assert!(a.is_empty());
807 }
808
809 #[test]
810 fn long_arg_with_equality_and_minus() {
811 let mut a = State::from(&["--speed=-12"]);
812 let s = a.take_arg(&long("speed"), true, M).unwrap().unwrap();
813 assert_eq!(s, "-12");
814 assert!(a.is_empty());
815 }
816
817 #[test]
818 fn short_arg_with_equality() {
819 let mut a = State::from(&["-s=12"]);
820 let s = a.take_arg(&short('s'), false, M).unwrap().unwrap();
821 assert_eq!(s, "12");
822 assert!(a.is_empty());
823 }
824
825 #[test]
826 fn short_arg_with_equality_and_minus() {
827 let mut a = State::from(&["-s=-12"]);
828 let s = a.take_arg(&short('s'), false, M).unwrap().unwrap();
829 assert_eq!(s, "-12");
830 assert!(a.is_empty());
831 }
832
833 #[test]
834 fn short_arg_with_equality_and_minus_is_adjacent() {
835 let mut a = State::from(&["-s=-12"]);
836 let s = a.take_arg(&short('s'), true, M).unwrap().unwrap();
837 assert_eq!(s, "-12");
838 assert!(a.is_empty());
839 }
840
841 #[test]
842 fn short_arg_without_equality() {
843 let mut a = State::from(&["-s", "12"]);
844 let s = a.take_arg(&short('s'), false, M).unwrap().unwrap();
845 assert_eq!(s, "12");
846 assert!(a.is_empty());
847 }
848
849 #[test]
850 fn two_short_flags() {
851 let mut a = State::from(&["-s", "-v"]);
852 assert!(a.take_flag(&short('s')));
853 assert!(a.take_flag(&short('v')));
854 assert!(a.is_empty());
855 }
856
857 #[test]
858 fn two_short_flags2() {
859 let mut a = State::from(&["-s", "-v"]);
860 assert!(a.take_flag(&short('v')));
861 assert!(!a.take_flag(&short('v')));
862 assert!(a.take_flag(&short('s')));
863 assert!(!a.take_flag(&short('s')));
864 assert!(a.is_empty());
865 }
866
867 #[test]
868 fn command_with_flags() {
869 let mut a = State::from(&["cmd", "-s", "v"]);
870 assert!(a.take_cmd("cmd"));
871 let s = a.take_arg(&short('s'), false, M).unwrap().unwrap();
872 assert_eq!(s, "v");
873 assert!(a.is_empty());
874 }
875
876 #[test]
877 fn command_and_positional() {
878 let mut a = State::from(&["cmd", "pos"]);
879 assert!(a.take_cmd("cmd"));
880 let w = a.take_positional_word(M).unwrap();
881 assert_eq!(w.2, "pos");
882 assert!(a.is_empty());
883 }
884
885 #[test]
886 fn positionals_after_double_dash1() {
887 let mut a = State::from(&["-v", "--", "-x"]);
888 assert!(a.take_flag(&short('v')));
889 let w = a.take_positional_word(M).unwrap();
890 assert_eq!(w.2, "-x");
891 assert!(a.is_empty());
892 }
893
894 #[test]
895 fn positionals_after_double_dash2() {
896 let mut a = State::from(&["-v", "--", "-x"]);
897 assert!(a.take_flag(&short('v')));
898 let w = a.take_positional_word(M).unwrap();
899 assert_eq!(w.2, "-x");
900 assert!(a.is_empty());
901 }
902
903 #[test]
904 fn positionals_after_double_dash3() {
905 let mut a = State::from(&["-v", "12", "--", "-x"]);
906 let w = a.take_arg(&short('v'), false, M).unwrap().unwrap();
907 assert_eq!(w, "12");
908 let w = a.take_positional_word(M).unwrap();
909 assert_eq!(w.2, "-x");
910 assert!(a.is_empty());
911 }
912
913 #[test]
914 fn ambiguity_towards_flag() {
915 let args = Args::from(&["-abc"]);
916 let mut err = None;
917 let mut a = State::construct(args, &['a', 'b', 'c'], &[], &mut err);
918
919 assert!(a.take_flag(&short('a')));
920 assert!(a.take_flag(&short('b')));
921 assert!(a.take_flag(&short('c')));
922 }
923
924 #[test]
925 fn ambiguity_towards_argument() {
926 let args = Args::from(&["-abc"]);
927 let mut err = None;
928 let mut a = State::construct(args, &[], &['a'], &mut err);
929
930 let r = a.take_arg(&short('a'), false, M).unwrap().unwrap();
931 assert_eq!(r, "bc");
932 }
933
934 #[test]
935 fn ambiguity_towards_error() {
936 let args = Args::from(&["-abc"]);
937 let mut err = None;
938 let _a = State::construct(args, &['a', 'b', 'c'], &['a'], &mut err);
939 assert!(err.is_some());
940 }
941
942 #[test]
943 fn ambiguity_towards_default() {
944 let a = State::from(&["-abc"]);
946 let is_ambig = matches!(a.peek(), Some(Arg::Word(_)));
947 assert!(is_ambig);
948 }
949}