1use crate::error::{utils, CapMode, ColorMode};
2use crate::help::Help;
3use crate::seqalin;
4use crate::seqalin::Cost;
5use crate::{arg::*, Command, Subcommand};
6use colored::Colorize;
7use stage::*;
8use std::collections::HashMap;
9use std::collections::HashSet;
10use std::marker::PhantomData;
11use std::ops::RangeBounds;
12use std::process::ExitCode;
13use std::str::FromStr;
14
15pub use crate::error::{Error, ErrorContext, ErrorKind};
16
17pub type Result<T> = std::result::Result<T, Error>;
19
20mod symbol {
21 pub const SWITCH: &str = "-";
23 pub const FLAG: &str = "--";
25}
26
27#[derive(Debug, Eq, Hash, PartialEq)]
28enum Tag<T: AsRef<str>> {
29 Switch(T),
30 Flag(T),
31}
32
33impl<T: AsRef<str>> Tag<T> {
34 fn as_ref(&self) -> &T {
35 match self {
36 Self::Flag(s) => s,
37 Self::Switch(s) => s,
38 }
39 }
40}
41
42#[derive(Debug, PartialEq)]
43enum Token {
44 UnattachedArgument(usize, String),
45 AttachedArgument(usize, String),
46 Flag(usize),
47 Switch(usize, char),
48 EmptySwitch(usize),
49 Ignore(usize, String),
50 Terminator(usize),
51}
52
53impl Token {
54 fn take_str(self) -> String {
55 match self {
56 Self::UnattachedArgument(_, s) => s,
57 Self::AttachedArgument(_, s) => s,
58 Self::Ignore(_, s) => s,
59 _ => panic!("cannot call take_str on token without string"),
60 }
61 }
62
63 fn _get_index_ref(&self) -> &usize {
64 match self {
65 Self::UnattachedArgument(i, _) => i,
66 Self::AttachedArgument(i, _) => i,
67 Self::Flag(i) => i,
68 Self::EmptySwitch(i) => i,
69 Self::Switch(i, _) => i,
70 Self::Terminator(i) => i,
71 Self::Ignore(i, _) => i,
72 }
73 }
74}
75
76#[derive(Debug, PartialEq)]
77struct Slot {
78 pointers: Vec<usize>,
79 visited: bool,
80}
81
82impl Slot {
83 fn new() -> Self {
84 Self {
85 pointers: Vec::new(),
86 visited: false,
87 }
88 }
89
90 fn push(&mut self, i: usize) -> () {
91 self.pointers.push(i);
92 }
93
94 fn is_visited(&self) -> bool {
95 self.visited
96 }
97
98 fn visit(&mut self) -> () {
99 self.visited = true;
100 }
101
102 fn get_indices(&self) -> &Vec<usize> {
103 &self.pointers
104 }
105
106 fn first(&self) -> Option<&usize> {
107 self.pointers.first()
108 }
109}
110
111#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
112enum MemoryState {
113 Start,
114 ProcessingFlags,
115 ProcessingOptionals,
116 ProcessingPositionals,
117 ProcessingSubcommands,
118 End,
119}
120
121impl MemoryState {
122 pub fn proceed(&mut self, mut next: MemoryState) {
124 if self > &mut next {
126 panic!("{}: argument discovery is in an invalid order: invalid state transition from {:?} to {:?}", "structural hazard".red().bold().underline(), self, next)
127 }
128 *self = next;
130 }
131
132 pub fn reset() -> Self {
133 Self::Start
134 }
135}
136
137pub mod stage {
138 pub trait ProcessorState {}
141
142 pub struct Build;
145
146 pub struct Ready;
149
150 pub struct Memory;
154
155 impl ProcessorState for Build {}
156
157 impl ProcessorState for Ready {}
158
159 impl ProcessorState for Memory {}
160}
161
162impl<S: ProcessorState> Cli<S> {
163 fn transition<T: ProcessorState>(self) -> Cli<T> {
165 Cli::<T> {
166 tokens: self.tokens,
167 store: self.store,
168 known_args: self.known_args,
169 asking_for_help: self.asking_for_help,
170 help: self.help,
171 state: self.state,
172 options: self.options,
173 _marker: PhantomData::<T>,
174 }
175 }
176}
177
178#[derive(Debug, PartialEq, Clone)]
179struct CliOptions {
180 pub prioritize_help: bool,
181 pub cap_mode: CapMode,
182 pub threshold: Cost,
183 pub capacity: usize,
184 pub color_mode: ColorMode,
185 pub err_prefix: String,
186 pub err_suffix: String,
187}
188
189impl CliOptions {
190 pub fn new() -> Self {
191 Self {
192 prioritize_help: true,
193 cap_mode: CapMode::new(),
194 threshold: 0,
195 capacity: 0,
196 color_mode: ColorMode::new(),
197 err_prefix: String::new(),
198 err_suffix: String::new(),
199 }
200 }
201}
202
203impl Default for CliOptions {
204 fn default() -> Self {
205 Self {
206 prioritize_help: true,
207 cap_mode: CapMode::default(),
208 threshold: 2,
209 capacity: 0,
210 color_mode: ColorMode::default(),
211 err_prefix: String::from(format!("{}: ", "error".red().bold())),
212 err_suffix: String::new(),
213 }
214 }
215}
216
217#[derive(Debug, PartialEq)]
219pub struct Cli<S: ProcessorState> {
220 tokens: Vec<Option<Token>>,
222 store: HashMap<Tag<String>, Slot>,
224 known_args: Vec<ArgType>,
226 asking_for_help: bool,
227 help: Option<Help>,
228 state: MemoryState,
229 options: CliOptions,
230 _marker: PhantomData<S>,
231}
232
233impl Default for Cli<Build> {
234 fn default() -> Self {
235 Self {
236 tokens: Vec::default(),
237 store: HashMap::default(),
238 known_args: Vec::default(),
239 help: None,
240 asking_for_help: false,
241 state: MemoryState::Start,
242 options: CliOptions::default(),
243 _marker: PhantomData,
244 }
245 }
246}
247
248impl Cli<Build> {
249 pub fn new() -> Self {
252 Self {
253 tokens: Vec::new(),
254 store: HashMap::new(),
255 known_args: Vec::new(),
256 help: None,
257 asking_for_help: false,
258 state: MemoryState::Start,
259 options: CliOptions::new(),
260 _marker: PhantomData,
261 }
262 }
263
264 pub fn with_capacity(mut self, cap: usize) -> Self {
267 self.options.capacity = cap;
268 self
269 }
270
271 pub fn threshold(mut self, cost: Cost) -> Self {
273 self.options.threshold = cost;
274 self
275 }
276
277 pub fn auto_uppercase_errors(mut self) -> Self {
279 self.options.cap_mode = CapMode::Upper;
280 self
281 }
282
283 pub fn auto_lowercase_errors(mut self) -> Self {
285 self.options.cap_mode = CapMode::Lower;
286 self
287 }
288
289 pub fn disable_auto_case_errors(mut self) -> Self {
291 self.options.cap_mode = CapMode::Manual;
292 self
293 }
294
295 pub fn enable_color(mut self) -> Self {
297 self.options.color_mode = ColorMode::On;
298 self
299 }
300
301 pub fn disable_color(mut self) -> Self {
303 self.options.color_mode = ColorMode::Off;
304 self
305 }
306
307 pub fn allow_color(mut self) -> Self {
310 self.options.color_mode = ColorMode::Normal;
311 self
312 }
313
314 pub fn deprioritize_help(mut self) -> Self {
319 self.options.prioritize_help = false;
320 self
321 }
322
323 pub fn prioritize_help(mut self) -> Self {
327 self.options.prioritize_help = true;
328 self
329 }
330
331 pub fn error_prefix<T: AsRef<str>>(mut self, prefix: T) -> Self {
334 self.options.err_prefix = String::from(prefix.as_ref());
335 self
336 }
337
338 pub fn error_suffix<T: AsRef<str>>(mut self, suffix: T) -> Self {
341 self.options.err_suffix = String::from(suffix.as_ref());
342 self
343 }
344
345 pub fn parse<T: Iterator<Item = String>>(mut self, args: T) -> Cli<Ready> {
350 self.options.color_mode.sync();
351 let mut tokens = Vec::<Option<Token>>::with_capacity(self.options.capacity);
352 let mut store = HashMap::with_capacity(self.options.capacity);
353 let mut terminated = false;
354 let mut args = args.skip(1).enumerate();
355 while let Some((i, mut arg)) = args.next() {
356 if terminated == true {
358 tokens.push(Some(Token::Ignore(i, arg)));
359 } else if arg.starts_with(symbol::SWITCH) == true {
361 let mut value: Option<String> = None;
363 let mut option: Option<String> = None;
364 {
365 if let Some((opt, val)) = arg.split_once('=') {
366 option = Some(opt.to_string());
367 value = Some(val.to_string());
368 }
369 }
370 if let Some(opt) = option {
372 arg = opt;
373 }
374 if arg.starts_with(symbol::FLAG) == true {
376 arg.replace_range(0..=1, "");
377 if arg.is_empty() == true {
379 tokens.push(Some(Token::Terminator(i)));
380 terminated = true;
381 } else {
383 store
384 .entry(Tag::Flag(arg))
385 .or_insert(Slot::new())
386 .push(tokens.len());
387 tokens.push(Some(Token::Flag(i)));
388 }
389 } else {
391 let mut arg = arg.chars().skip(1);
393 if let Some(c) = arg.next() {
395 store
396 .entry(Tag::Switch(c.to_string()))
397 .or_insert(Slot::new())
398 .push(tokens.len());
399 tokens.push(Some(Token::Switch(i, c)));
400 } else {
401 store
402 .entry(Tag::Switch(String::new()))
403 .or_insert(Slot::new())
404 .push(tokens.len());
405 tokens.push(Some(Token::EmptySwitch(i)));
406 }
407 while let Some(c) = arg.next() {
409 store
410 .entry(Tag::Switch(c.to_string()))
411 .or_insert(Slot::new())
412 .push(tokens.len());
413 tokens.push(Some(Token::Switch(i, c)));
414 }
415 }
416 if let Some(val) = value {
418 tokens.push(Some(Token::AttachedArgument(i, val)));
419 }
420 } else {
422 tokens.push(Some(Token::UnattachedArgument(i, arg)));
423 }
424 }
425 self.tokens = tokens;
426 self.store = store;
427 Cli::transition(self)
429 }
430}
431
432impl Cli<Ready> {
433 pub fn go<T: Command>(self) -> ExitCode {
447 let mut cli: Cli<Memory> = self.save();
448
449 match T::interpret(&mut cli) {
450 Ok(program) => {
452 match cli.empty() {
454 Ok(_) => {
455 let cli_opts = cli.options.clone();
456 std::mem::drop(cli);
457 match program.execute() {
458 Ok(_) => ExitCode::from(0),
459 Err(err) => {
460 eprintln!(
461 "{}{}{}",
462 cli_opts.err_prefix,
463 utils::format_err_msg(err.to_string(), cli_opts.cap_mode),
464 cli_opts.err_suffix
465 );
466 ExitCode::from(101)
467 }
468 }
469 }
470 Err(err) => {
472 let cli_opts = cli.options;
473 match err.kind() {
474 ErrorKind::Help => println!("{}", &err),
475 _ => eprintln!(
476 "{}{}{}",
477 cli_opts.err_prefix,
478 utils::format_err_msg(err.to_string(), cli_opts.cap_mode),
479 cli_opts.err_suffix
480 ),
481 }
482 ExitCode::from(err.code())
483 }
484 }
485 }
486 Err(err) => {
488 let cli_opts = cli.options;
489 match err.kind() {
490 ErrorKind::Help => println!("{}", &err),
491 _ => eprintln!(
492 "{}{}{}",
493 cli_opts.err_prefix,
494 utils::format_err_msg(err.to_string(), cli_opts.cap_mode),
495 cli_opts.err_suffix
496 ),
497 }
498 ExitCode::from(err.code())
499 }
500 }
501 }
502
503 pub fn save(self) -> Cli<Memory> {
506 self.transition()
507 }
508}
509
510impl Cli<Memory> {
513 pub fn is_empty(&self) -> bool {
516 self.tokens.len() == 0
517 }
518
519 pub fn help(&mut self, help: Help) -> Result<bool> {
524 self.help = Some(help);
525 if self.asking_for_help == false && self.is_help_enabled() == true {
527 self.asking_for_help = self.check(self.help.as_ref().unwrap().get_arg())?;
528 }
529 Ok(self.asking_for_help)
530 }
531
532 pub fn raise_help(&self) -> Result<()> {
535 self.try_to_help()
536 }
537
538 pub fn lower_help(&mut self) -> () {
540 self.asking_for_help = false;
541 }
542
543 pub fn unset_help(&mut self) -> () {
545 self.help = None;
546 }
547
548 pub fn nest<'a, T: Subcommand<U>, U>(
552 &mut self,
553 subcommand: Arg<Callable>,
554 ) -> Result<Option<T>> {
555 self.known_args.push(ArgType::from(subcommand));
556 let command_exists = self
558 .tokens
559 .iter()
560 .find(|f| match f {
561 Some(Token::UnattachedArgument(_, _)) => true,
562 _ => false,
563 })
564 .is_some();
565 if command_exists == true {
566 self.state = MemoryState::reset();
568 let sub = Some(T::interpret(self)?);
569 self.state.proceed(MemoryState::ProcessingSubcommands);
570 Ok(sub)
571 } else {
572 self.state.proceed(MemoryState::ProcessingSubcommands);
573 return Ok(None);
574 }
575 }
576
577 pub fn select<T: AsRef<str> + std::cmp::PartialEq>(&mut self, bank: &[T]) -> Result<String> {
586 let i: usize = self
588 .tokens
589 .iter()
590 .find_map(|f| match f {
591 Some(Token::UnattachedArgument(i, _)) => Some(*i),
592 _ => None,
593 })
594 .expect("an unattached argument must exist before calling `match(...)`");
595 let command = self
596 .next_uarg()
597 .expect("`nest(...)` must be called before this function");
598
599 let ooc_arg = self.capture_bad_flag(i)?;
601
602 if bank.iter().find(|p| p.as_ref() == command).is_some() {
603 if let Some((prefix, key, pos)) = ooc_arg {
604 if pos < i {
605 self.try_to_help()?;
606 return Err(Error::new(
607 self.help.clone(),
608 ErrorKind::OutOfContextArgSuggest,
609 ErrorContext::OutofContextArgSuggest(format!("{}{}", prefix, key), command),
610 self.options.cap_mode,
611 ));
612 }
613 }
614 Ok(command)
615 } else {
617 if let Some(w) = if self.options.threshold > 0 {
619 seqalin::sel_min_edit_str(&command, &bank, self.options.threshold)
620 } else {
621 None
622 } {
623 Err(Error::new(
624 self.help.clone(),
625 ErrorKind::SuggestSubcommand,
626 ErrorContext::SuggestWord(command, w.to_string()),
627 self.options.cap_mode,
628 ))
629 } else {
630 self.try_to_help()?;
631 Err(Error::new(
632 self.help.clone(),
633 ErrorKind::UnknownSubcommand,
634 ErrorContext::UnknownSubcommand(
635 self.known_args.pop().expect("requires positional argument"),
636 command,
637 ),
638 self.options.cap_mode,
639 ))
640 }
641 }
642 }
643
644 pub fn check<'a>(&mut self, arg: Arg<Raisable>) -> Result<bool> {
654 match ArgType::from(arg) {
655 ArgType::Flag(fla) => self.check_flag(fla),
656 _ => panic!("impossible code condition"),
657 }
658 }
659
660 pub fn check_all<'a>(&mut self, arg: Arg<Raisable>) -> Result<usize> {
669 match ArgType::from(arg) {
670 ArgType::Flag(fla) => self.check_flag_all(fla),
671 _ => panic!("impossible code condition"),
672 }
673 }
674
675 pub fn check_until<'a>(&mut self, arg: Arg<Raisable>, limit: usize) -> Result<usize> {
686 match ArgType::from(arg) {
687 ArgType::Flag(fla) => self.check_flag_until(fla, limit),
688 _ => panic!("impossible code condition"),
689 }
690 }
691
692 pub fn check_between<'a, R: RangeBounds<usize>>(
703 &mut self,
704 arg: Arg<Raisable>,
705 span: R,
706 ) -> Result<usize> {
707 match ArgType::from(arg) {
708 ArgType::Flag(fla) => self.check_flag_between(fla, span),
709 _ => panic!("impossible code condition"),
710 }
711 }
712
713 pub fn get<'a, T: FromStr>(&mut self, arg: Arg<Valuable>) -> Result<Option<T>>
723 where
724 <T as FromStr>::Err: 'static + std::error::Error,
725 {
726 match ArgType::from(arg) {
727 ArgType::Optional(opt) => self.get_option(opt),
728 ArgType::Positional(pos) => self.get_positional(pos),
729 _ => panic!("impossible code condition"),
730 }
731 }
732
733 pub fn get_all<'a, T: FromStr>(&mut self, arg: Arg<Valuable>) -> Result<Option<Vec<T>>>
743 where
744 <T as FromStr>::Err: 'static + std::error::Error,
745 {
746 match ArgType::from(arg) {
747 ArgType::Optional(opt) => self.get_option_all(opt),
748 ArgType::Positional(pos) => self.get_positional_all(pos),
749 _ => panic!("impossible code condition"),
750 }
751 }
752
753 pub fn get_until<'a, T: FromStr>(
764 &mut self,
765 arg: Arg<Valuable>,
766 limit: usize,
767 ) -> Result<Option<Vec<T>>>
768 where
769 <T as FromStr>::Err: 'static + std::error::Error,
770 {
771 match ArgType::from(arg) {
772 ArgType::Optional(opt) => self.get_option_until(opt, limit),
773 ArgType::Positional(pos) => self.get_positional_until(pos, limit),
774 _ => panic!("impossible code condition"),
775 }
776 }
777
778 pub fn get_between<'a, T: FromStr, R: RangeBounds<usize>>(
789 &mut self,
790 arg: Arg<Valuable>,
791 span: R,
792 ) -> Result<Option<Vec<T>>>
793 where
794 <T as FromStr>::Err: 'static + std::error::Error,
795 {
796 match ArgType::from(arg) {
797 ArgType::Optional(opt) => self.get_option_between(opt, span),
798 ArgType::Positional(pos) => self.get_positional_between(pos, span),
799 _ => panic!("impossible code condition"),
800 }
801 }
802
803 pub fn require<'a, T: FromStr>(&mut self, arg: Arg<Valuable>) -> Result<T>
811 where
812 <T as FromStr>::Err: 'static + std::error::Error,
813 {
814 match ArgType::from(arg) {
815 ArgType::Optional(opt) => self.require_option(opt),
816 ArgType::Positional(pos) => self.require_positional(pos),
817 _ => panic!("impossible code condition"),
818 }
819 }
820
821 pub fn require_all<'a, T: FromStr>(&mut self, arg: Arg<Valuable>) -> Result<Vec<T>>
830 where
831 <T as FromStr>::Err: 'static + std::error::Error,
832 {
833 match ArgType::from(arg) {
834 ArgType::Optional(opt) => self.require_option_all(opt),
835 ArgType::Positional(pos) => self.require_positional_all(pos),
836 _ => panic!("impossible code condition"),
837 }
838 }
839
840 pub fn require_until<'a, T: FromStr>(
850 &mut self,
851 arg: Arg<Valuable>,
852 limit: usize,
853 ) -> Result<Vec<T>>
854 where
855 <T as FromStr>::Err: 'static + std::error::Error,
856 {
857 match ArgType::from(arg) {
858 ArgType::Optional(opt) => self.require_option_until(opt, limit),
859 ArgType::Positional(pos) => self.require_positional_until(pos, limit),
860 _ => panic!("impossible code condition"),
861 }
862 }
863
864 pub fn require_between<'a, T: FromStr, R: RangeBounds<usize>>(
874 &mut self,
875 arg: Arg<Valuable>,
876 span: R,
877 ) -> Result<Vec<T>>
878 where
879 <T as FromStr>::Err: 'static + std::error::Error,
880 {
881 match ArgType::from(arg) {
882 ArgType::Optional(opt) => self.require_option_between(opt, span),
883 ArgType::Positional(pos) => self.require_positional_between(pos, span),
884 _ => panic!("impossible code condition"),
885 }
886 }
887
888 pub fn empty<'a>(&'a mut self) -> Result<()> {
894 self.state.proceed(MemoryState::End);
895 self.try_to_help()?;
896 if let Some((prefix, key, _)) = self.capture_bad_flag(self.tokens.len())? {
898 Err(Error::new(
899 self.help.clone(),
900 ErrorKind::UnexpectedArg,
901 ErrorContext::UnexpectedArg(format!("{}{}", prefix, key)),
902 self.options.cap_mode,
903 ))
904 } else if let Some(t) = self.tokens.iter().find(|p| p.is_some()) {
906 match t {
907 Some(Token::UnattachedArgument(_, word)) => Err(Error::new(
908 self.help.clone(),
909 ErrorKind::UnexpectedArg,
910 ErrorContext::UnexpectedArg(word.to_string()),
911 self.options.cap_mode,
912 )),
913 Some(Token::Terminator(_)) => Err(Error::new(
914 self.help.clone(),
915 ErrorKind::UnexpectedArg,
916 ErrorContext::UnexpectedArg(symbol::FLAG.to_string()),
917 self.options.cap_mode,
918 )),
919 _ => panic!("no other tokens types should be left"),
920 }
921 } else {
922 Ok(())
923 }
924 }
925
926 pub fn remainder(&mut self) -> Result<Vec<String>> {
934 self.tokens
935 .iter_mut()
936 .skip_while(|tkn| match tkn {
937 Some(Token::Terminator(_)) => false,
938 _ => true,
939 })
940 .filter_map(|tkn| {
941 match tkn {
942 Some(Token::Terminator(_)) => {
944 tkn.take().unwrap();
945 None
946 }
947 Some(Token::Ignore(_, _)) => Some(Ok(tkn.take().unwrap().take_str())),
948 Some(Token::AttachedArgument(_, _)) => Some(Err(Error::new(
949 self.help.clone(),
950 ErrorKind::UnexpectedValue,
951 ErrorContext::UnexpectedValue(
952 ArgType::Flag(Flag::new("")),
953 tkn.take().unwrap().take_str(),
954 ),
955 self.options.cap_mode,
956 ))),
957 _ => panic!("no other tokens should exist beyond terminator {:?}", tkn),
958 }
959 })
960 .collect()
961 }
962}
963
964impl Cli<Memory> {
967 fn get_positional<'a, T: FromStr>(&mut self, p: Positional) -> Result<Option<T>>
972 where
973 <T as FromStr>::Err: 'static + std::error::Error,
974 {
975 self.state.proceed(MemoryState::ProcessingPositionals);
976 self.known_args.push(ArgType::Positional(p));
977 self.try_positional()
978 }
979
980 fn get_positional_all<'a, T: FromStr>(&mut self, p: Positional) -> Result<Option<Vec<T>>>
981 where
982 <T as FromStr>::Err: 'static + std::error::Error,
983 {
984 self.state.proceed(MemoryState::ProcessingPositionals);
985 let mut result = Vec::<T>::new();
986 match self.get_positional(p)? {
987 Some(item) => result.push(item),
988 None => return Ok(None),
989 }
990 while let Some(v) = self.try_positional()? {
991 result.push(v);
992 }
993 Ok(Some(result))
994 }
995
996 fn get_positional_until<'a, T: FromStr>(
997 &mut self,
998 p: Positional,
999 limit: usize,
1000 ) -> Result<Option<Vec<T>>>
1001 where
1002 <T as FromStr>::Err: 'static + std::error::Error,
1003 {
1004 self.state.proceed(MemoryState::ProcessingPositionals);
1005 let values = self.get_positional_all::<T>(p)?;
1006 match values {
1007 Some(r) => match r.len() <= limit {
1009 true => Ok(Some(r)),
1010 false => Err(Error::new(
1011 self.help.clone(),
1012 ErrorKind::ExceedingMaxCount,
1013 ErrorContext::ExceededThreshold(self.known_args.pop().unwrap(), r.len(), limit),
1014 self.options.cap_mode,
1015 )),
1016 },
1017 None => Ok(None),
1018 }
1019 }
1020
1021 fn get_positional_between<'a, T: FromStr, R: RangeBounds<usize>>(
1022 &mut self,
1023 p: Positional,
1024 span: R,
1025 ) -> Result<Option<Vec<T>>>
1026 where
1027 <T as FromStr>::Err: 'static + std::error::Error,
1028 {
1029 self.state.proceed(MemoryState::ProcessingPositionals);
1030 let values = self.get_positional_all::<T>(p)?;
1031 match values {
1032 Some(r) => match span.contains(&r.len()) {
1034 true => Ok(Some(r)),
1035 false => Err(Error::new(
1036 self.help.clone(),
1037 ErrorKind::OutsideRange,
1038 ErrorContext::OutsideRange(
1039 self.known_args.pop().unwrap(),
1040 r.len(),
1041 span.start_bound().cloned(),
1042 span.end_bound().cloned(),
1043 ),
1044 self.options.cap_mode,
1045 )),
1046 },
1047 None => Ok(None),
1048 }
1049 }
1050
1051 fn require_positional<'a, T: FromStr>(&mut self, p: Positional) -> Result<T>
1055 where
1056 <T as FromStr>::Err: 'static + std::error::Error,
1057 {
1058 self.state.proceed(MemoryState::ProcessingPositionals);
1059 if let Some(value) = self.get_positional(p)? {
1060 Ok(value)
1061 } else {
1062 self.try_to_help()?;
1063 self.empty()?;
1064 Err(Error::new(
1065 self.help.clone(),
1066 ErrorKind::MissingPositional,
1067 ErrorContext::FailedArg(self.known_args.pop().unwrap()),
1068 self.options.cap_mode,
1069 ))
1070 }
1071 }
1072
1073 fn require_positional_all<'a, T: FromStr>(&mut self, p: Positional) -> Result<Vec<T>>
1079 where
1080 <T as FromStr>::Err: 'static + std::error::Error,
1081 {
1082 self.state.proceed(MemoryState::ProcessingPositionals);
1083 let mut result = Vec::<T>::new();
1084 result.push(self.require_positional(p)?);
1085 while let Some(v) = self.try_positional()? {
1086 result.push(v);
1087 }
1088 Ok(result)
1089 }
1090
1091 fn require_positional_until<'a, T: FromStr>(
1092 &mut self,
1093 p: Positional,
1094 limit: usize,
1095 ) -> Result<Vec<T>>
1096 where
1097 <T as FromStr>::Err: 'static + std::error::Error,
1098 {
1099 self.state.proceed(MemoryState::ProcessingPositionals);
1100 let values = self.require_positional_all(p)?;
1101 match values.len() <= limit {
1103 true => Ok(values),
1104 false => Err(Error::new(
1105 self.help.clone(),
1106 ErrorKind::ExceedingMaxCount,
1107 ErrorContext::ExceededThreshold(
1108 self.known_args.pop().unwrap(),
1109 values.len(),
1110 limit,
1111 ),
1112 self.options.cap_mode,
1113 )),
1114 }
1115 }
1116
1117 fn require_positional_between<'a, T: FromStr, R: RangeBounds<usize>>(
1118 &mut self,
1119 p: Positional,
1120 span: R,
1121 ) -> Result<Vec<T>>
1122 where
1123 <T as FromStr>::Err: 'static + std::error::Error,
1124 {
1125 self.state.proceed(MemoryState::ProcessingPositionals);
1126 let values = self.require_positional_all::<T>(p)?;
1127 match span.contains(&values.len()) {
1128 true => Ok(values),
1129 false => Err(Error::new(
1130 self.help.clone(),
1131 ErrorKind::OutsideRange,
1132 ErrorContext::OutsideRange(
1133 self.known_args.pop().unwrap(),
1134 values.len(),
1135 span.start_bound().cloned(),
1136 span.end_bound().cloned(),
1137 ),
1138 self.options.cap_mode,
1139 )),
1140 }
1141 }
1142
1143 fn get_option<'a, T: FromStr>(&mut self, o: Optional) -> Result<Option<T>>
1147 where
1148 <T as FromStr>::Err: 'static + std::error::Error,
1149 {
1150 self.state.proceed(MemoryState::ProcessingOptionals);
1151 let mut locs = self.take_flag_locs(o.get_flag().get_name());
1153 if let Some(c) = o.get_flag().get_switch() {
1154 locs.extend(self.take_switch_locs(c));
1155 }
1156 self.known_args.push(ArgType::Optional(o));
1157 let mut values = self.pull_flag(locs, true);
1159 match values.len() {
1160 1 => {
1161 if let Some(word) = values.pop().unwrap() {
1162 let result = word.parse::<T>();
1163 match result {
1164 Ok(r) => Ok(Some(r)),
1165 Err(err) => {
1166 self.try_to_help()?;
1167 Err(Error::new(
1168 self.help.clone(),
1169 ErrorKind::BadType,
1170 ErrorContext::FailedCast(
1171 self.known_args.pop().unwrap(),
1172 word,
1173 Box::new(err),
1174 ),
1175 self.options.cap_mode,
1176 ))
1177 }
1178 }
1179 } else {
1180 self.try_to_help()?;
1181 Err(Error::new(
1182 self.help.clone(),
1183 ErrorKind::ExpectingValue,
1184 ErrorContext::FailedArg(self.known_args.pop().unwrap()),
1185 self.options.cap_mode,
1186 ))
1187 }
1188 }
1189 0 => Ok(None),
1190 _ => {
1191 self.try_to_help()?;
1192 Err(Error::new(
1193 self.help.clone(),
1194 ErrorKind::DuplicateOptions,
1195 ErrorContext::FailedArg(self.known_args.pop().unwrap()),
1196 self.options.cap_mode,
1197 ))
1198 }
1199 }
1200 }
1201
1202 fn get_option_all<'a, T: FromStr>(&mut self, o: Optional) -> Result<Option<Vec<T>>>
1206 where
1207 <T as FromStr>::Err: 'static + std::error::Error,
1208 {
1209 self.state.proceed(MemoryState::ProcessingOptionals);
1210 let mut locs = self.take_flag_locs(o.get_flag().get_name());
1212 if let Some(c) = o.get_flag().get_switch() {
1213 locs.extend(self.take_switch_locs(c));
1214 }
1215 self.known_args.push(ArgType::Optional(o));
1216 let values = self.pull_flag(locs, true);
1218 if values.is_empty() == true {
1219 return Ok(None);
1220 }
1221 let mut transform = Vec::<T>::with_capacity(values.len());
1223 for val in values {
1224 if let Some(word) = val {
1225 let result = word.parse::<T>();
1226 match result {
1227 Ok(r) => transform.push(r),
1228 Err(err) => {
1229 self.try_to_help()?;
1230 return Err(Error::new(
1231 self.help.clone(),
1232 ErrorKind::BadType,
1233 ErrorContext::FailedCast(
1234 self.known_args.pop().unwrap(),
1235 word,
1236 Box::new(err),
1237 ),
1238 self.options.cap_mode,
1239 ));
1240 }
1241 }
1242 } else {
1243 self.try_to_help()?;
1244 return Err(Error::new(
1245 self.help.clone(),
1246 ErrorKind::ExpectingValue,
1247 ErrorContext::FailedArg(self.known_args.pop().unwrap()),
1248 self.options.cap_mode,
1249 ));
1250 }
1251 }
1252 Ok(Some(transform))
1253 }
1254
1255 fn get_option_until<'a, T: FromStr>(
1259 &mut self,
1260 o: Optional,
1261 limit: usize,
1262 ) -> Result<Option<Vec<T>>>
1263 where
1264 <T as FromStr>::Err: 'static + std::error::Error,
1265 {
1266 self.state.proceed(MemoryState::ProcessingOptionals);
1267 let values = self.get_option_all::<T>(o)?;
1268 match values {
1269 Some(r) => match r.len() <= limit {
1271 true => Ok(Some(r)),
1272 false => Err(Error::new(
1273 self.help.clone(),
1274 ErrorKind::ExceedingMaxCount,
1275 ErrorContext::ExceededThreshold(self.known_args.pop().unwrap(), r.len(), limit),
1276 self.options.cap_mode,
1277 )),
1278 },
1279 None => Ok(None),
1280 }
1281 }
1282
1283 fn get_option_between<'a, T: FromStr, R: RangeBounds<usize>>(
1284 &mut self,
1285 o: Optional,
1286 span: R,
1287 ) -> Result<Option<Vec<T>>>
1288 where
1289 <T as FromStr>::Err: 'static + std::error::Error,
1290 {
1291 self.state.proceed(MemoryState::ProcessingOptionals);
1292 let values = self.get_option_all::<T>(o)?;
1293 match values {
1294 Some(r) => match span.contains(&r.len()) {
1296 true => Ok(Some(r)),
1297 false => Err(Error::new(
1298 self.help.clone(),
1299 ErrorKind::OutsideRange,
1300 ErrorContext::OutsideRange(
1301 self.known_args.pop().unwrap(),
1302 r.len(),
1303 span.start_bound().cloned(),
1304 span.end_bound().cloned(),
1305 ),
1306 self.options.cap_mode,
1307 )),
1308 },
1309 None => Ok(None),
1310 }
1311 }
1312
1313 fn require_option<'a, T: FromStr>(&mut self, o: Optional) -> Result<T>
1315 where
1316 <T as FromStr>::Err: 'static + std::error::Error,
1317 {
1318 self.state.proceed(MemoryState::ProcessingOptionals);
1319 if let Some(value) = self.get_option(o)? {
1320 Ok(value)
1321 } else {
1322 self.try_to_help()?;
1323 self.empty()?;
1324 Err(Error::new(
1325 self.help.clone(),
1326 ErrorKind::MissingOption,
1327 ErrorContext::FailedArg(self.known_args.pop().unwrap()),
1328 self.options.cap_mode,
1329 ))
1330 }
1331 }
1332
1333 fn require_option_all<'a, T: FromStr>(&mut self, o: Optional) -> Result<Vec<T>>
1334 where
1335 <T as FromStr>::Err: 'static + std::error::Error,
1336 {
1337 self.state.proceed(MemoryState::ProcessingOptionals);
1338 if let Some(value) = self.get_option_all(o)? {
1339 Ok(value)
1340 } else {
1341 self.try_to_help()?;
1342 self.empty()?;
1343 Err(Error::new(
1344 self.help.clone(),
1345 ErrorKind::MissingOption,
1346 ErrorContext::FailedArg(self.known_args.pop().unwrap()),
1347 self.options.cap_mode,
1348 ))
1349 }
1350 }
1351
1352 fn require_option_until<'a, T: FromStr>(&mut self, o: Optional, limit: usize) -> Result<Vec<T>>
1353 where
1354 <T as FromStr>::Err: 'static + std::error::Error,
1355 {
1356 self.state.proceed(MemoryState::ProcessingOptionals);
1357 let values = self.require_option_all(o)?;
1358 match values.len() <= limit {
1360 true => Ok(values),
1361 false => Err(Error::new(
1362 self.help.clone(),
1363 ErrorKind::ExceedingMaxCount,
1364 ErrorContext::ExceededThreshold(
1365 self.known_args.pop().unwrap(),
1366 values.len(),
1367 limit,
1368 ),
1369 self.options.cap_mode,
1370 )),
1371 }
1372 }
1373
1374 fn require_option_between<'a, T: FromStr, R: RangeBounds<usize>>(
1375 &mut self,
1376 o: Optional,
1377 span: R,
1378 ) -> Result<Vec<T>>
1379 where
1380 <T as FromStr>::Err: 'static + std::error::Error,
1381 {
1382 self.state.proceed(MemoryState::ProcessingOptionals);
1383 let values = self.require_option_all::<T>(o)?;
1384 match span.contains(&values.len()) {
1385 true => Ok(values),
1386 false => Err(Error::new(
1387 self.help.clone(),
1388 ErrorKind::OutsideRange,
1389 ErrorContext::OutsideRange(
1390 self.known_args.pop().unwrap(),
1391 values.len(),
1392 span.start_bound().cloned(),
1393 span.end_bound().cloned(),
1394 ),
1395 self.options.cap_mode,
1396 )),
1397 }
1398 }
1399
1400 fn check_flag<'a>(&mut self, f: Flag) -> Result<bool> {
1404 self.state.proceed(MemoryState::ProcessingFlags);
1405 let occurences = self.check_flag_all(f)?;
1406 match occurences > 1 {
1407 true => {
1408 self.try_to_help()?;
1409 Err(Error::new(
1410 self.help.clone(),
1411 ErrorKind::DuplicateOptions,
1412 ErrorContext::FailedArg(self.known_args.pop().unwrap()),
1413 self.options.cap_mode,
1414 ))
1415 }
1416 false => Ok(occurences == 1),
1418 }
1419 }
1420
1421 fn check_flag_all<'a>(&mut self, f: Flag) -> Result<usize> {
1425 self.state.proceed(MemoryState::ProcessingFlags);
1426 let mut locs = self.take_flag_locs(f.get_name());
1428 if let Some(c) = f.get_switch() {
1430 locs.extend(self.take_switch_locs(c));
1431 };
1432 self.known_args.push(ArgType::Flag(f));
1433 let mut occurences = self.pull_flag(locs, false);
1434 if let Some(val) = occurences.iter_mut().find(|p| p.is_some()) {
1436 self.try_to_help()?;
1437 return Err(Error::new(
1438 self.help.clone(),
1439 ErrorKind::UnexpectedValue,
1440 ErrorContext::UnexpectedValue(self.known_args.pop().unwrap(), val.take().unwrap()),
1441 self.options.cap_mode,
1442 ));
1443 } else {
1444 let raised = occurences.len() != 0;
1445 if let Some(hp) = &self.help {
1447 if raised == true
1448 && ArgType::from(hp.get_arg()).into_flag().unwrap().get_name()
1449 == self
1450 .known_args
1451 .last()
1452 .unwrap()
1453 .as_flag()
1454 .unwrap()
1455 .get_name()
1456 {
1457 self.asking_for_help = true;
1458 }
1459 }
1460 Ok(occurences.len())
1462 }
1463 }
1464
1465 fn check_flag_until<'a>(&mut self, f: Flag, limit: usize) -> Result<usize> {
1469 self.state.proceed(MemoryState::ProcessingFlags);
1470 let occurences = self.check_flag_all(f)?;
1471 match occurences <= limit {
1473 true => Ok(occurences),
1474 false => Err(Error::new(
1475 self.help.clone(),
1476 ErrorKind::ExceedingMaxCount,
1477 ErrorContext::ExceededThreshold(self.known_args.pop().unwrap(), occurences, limit),
1478 self.options.cap_mode,
1479 )),
1480 }
1481 }
1482
1483 fn check_flag_between<'a, R: RangeBounds<usize>>(&mut self, f: Flag, span: R) -> Result<usize> {
1484 self.state.proceed(MemoryState::ProcessingFlags);
1485 let occurences = self.check_flag_all(f)?;
1486 match span.contains(&occurences) {
1488 true => Ok(occurences),
1489 false => Err(Error::new(
1490 self.help.clone(),
1491 ErrorKind::OutsideRange,
1492 ErrorContext::OutsideRange(
1493 self.known_args.pop().unwrap(),
1494 occurences,
1495 span.start_bound().cloned(),
1496 span.end_bound().cloned(),
1497 ),
1498 self.options.cap_mode,
1499 )),
1500 }
1501 }
1502}
1503
1504impl Cli<Memory> {
1507 fn try_positional<'a, T: FromStr>(&mut self) -> Result<Option<T>>
1511 where
1512 <T as FromStr>::Err: 'static + std::error::Error,
1513 {
1514 match self.next_uarg() {
1515 Some(word) => match word.parse::<T>() {
1516 Ok(r) => Ok(Some(r)),
1517 Err(err) => {
1518 self.try_to_help()?;
1519 self.prioritize_suggestion()?;
1520 Err(Error::new(
1521 self.help.clone(),
1522 ErrorKind::BadType,
1523 ErrorContext::FailedCast(
1524 self.known_args.pop().unwrap(),
1525 word,
1526 Box::new(err),
1527 ),
1528 self.options.cap_mode,
1529 ))
1530 }
1531 },
1532 None => Ok(None),
1533 }
1534 }
1535
1536 fn known_args_as_flag_names(&self) -> HashSet<&str> {
1541 self.known_args
1543 .iter()
1544 .filter_map(|f| match f {
1545 ArgType::Flag(f) => Some(f.get_name()),
1546 ArgType::Optional(o) => Some(o.get_flag().get_name()),
1547 _ => None,
1548 })
1549 .collect()
1550 }
1551
1552 fn find_first_flag_left(&self, breakpoint: usize) -> Option<(&str, usize)> {
1557 let mut min_i: Option<(&str, usize)> = None;
1558 let mut opt_it = self
1559 .store
1560 .iter()
1561 .filter(|(_, slot)| slot.is_visited() == false);
1562 while let Some((key, val)) = opt_it.next() {
1563 min_i = if *val.first().unwrap() < breakpoint
1565 && (min_i.is_none() || min_i.unwrap().1 > *val.first().unwrap())
1566 {
1567 Some((key.as_ref(), *val.first().unwrap()))
1568 } else {
1569 min_i
1570 };
1571 }
1572 min_i
1573 }
1574
1575 fn capture_bad_flag<'a>(&self, i: usize) -> Result<Option<(&str, &str, usize)>> {
1577 if let Some((key, val)) = self.find_first_flag_left(i) {
1578 self.try_to_help()?;
1579 if let Some(t) = self.tokens.get(val).unwrap() {
1581 let prefix = match t {
1582 Token::Switch(_, _) | Token::EmptySwitch(_) => symbol::SWITCH,
1583 Token::Flag(_) => {
1584 let bank: Vec<&str> = self.known_args_as_flag_names().into_iter().collect();
1586 if let Some(closest) = if self.options.threshold > 0 {
1587 seqalin::sel_min_edit_str(key, &bank, self.options.threshold)
1588 } else {
1589 None
1590 } {
1591 return Err(Error::new(
1592 self.help.clone(),
1593 ErrorKind::SuggestArg,
1594 ErrorContext::SuggestWord(
1595 format!("{}{}", symbol::FLAG, key),
1596 format!("{}{}", symbol::FLAG, closest),
1597 ),
1598 self.options.cap_mode,
1599 ));
1600 }
1601 symbol::FLAG
1602 }
1603 _ => panic!("no other tokens are allowed in hashmap"),
1604 };
1605 Ok(Some((prefix, key, val)))
1606 } else {
1607 panic!("this token's values have been removed")
1608 }
1609 } else {
1610 Ok(None)
1611 }
1612 }
1613
1614 fn take_flag_locs(&mut self, tag: &str) -> Vec<usize> {
1618 if let Some(slot) = self.store.get_mut(&Tag::Flag(tag.to_owned())) {
1619 slot.visit();
1620 slot.get_indices().to_vec()
1621 } else {
1622 Vec::new()
1623 }
1624 }
1625
1626 fn take_switch_locs(&mut self, c: &char) -> Vec<usize> {
1628 let mut arr = [0; 4];
1630 let tag = c.encode_utf8(&mut arr);
1631
1632 if let Some(slot) = self.store.get_mut(&Tag::Switch(tag.to_owned())) {
1633 slot.visit();
1634 slot.get_indices().to_vec()
1635 } else {
1636 Vec::new()
1637 }
1638 }
1639
1640 fn prioritize_suggestion(&self) -> Result<()> {
1644 let mut kv: Vec<(&String, &Vec<usize>)> = self
1645 .store
1646 .iter()
1647 .map(|(tag, slot)| (tag.as_ref(), slot.get_indices()))
1648 .collect::<Vec<(&String, &Vec<usize>)>>();
1649 kv.sort_by(|a, b| a.1.first().unwrap().cmp(b.1.first().unwrap()));
1650 let bank: Vec<&str> = self.known_args_as_flag_names().into_iter().collect();
1651 let r = kv
1652 .iter()
1653 .find_map(|f| match self.tokens.get(*f.1.first().unwrap()).unwrap() {
1654 Some(Token::Flag(_)) => {
1655 if let Some(word) = if self.options.threshold > 0 {
1656 seqalin::sel_min_edit_str(f.0, &bank, self.options.threshold)
1657 } else {
1658 None
1659 } {
1660 Some(Error::new(
1661 self.help.clone(),
1662 ErrorKind::SuggestArg,
1663 ErrorContext::SuggestWord(
1664 format!("{}{}", symbol::FLAG, f.0),
1665 format!("{}{}", symbol::FLAG, word),
1666 ),
1667 self.options.cap_mode,
1668 ))
1669 } else {
1670 None
1671 }
1672 }
1673 _ => None,
1674 });
1675 if self.asking_for_help == true {
1676 Ok(())
1677 } else if let Some(e) = r {
1678 Err(e)
1679 } else {
1680 Ok(())
1681 }
1682 }
1683
1684 fn pull_flag(&mut self, locations: Vec<usize>, with_uarg: bool) -> Vec<Option<String>> {
1688 locations
1690 .iter()
1691 .map(|i| {
1692 self.tokens.get_mut(*i).unwrap().take();
1694 if let Some(t_next) = self.tokens.get_mut(*i + 1) {
1696 match t_next {
1697 Some(Token::AttachedArgument(_, _)) => {
1698 Some(t_next.take().unwrap().take_str())
1699 }
1700 Some(Token::UnattachedArgument(_, _)) => {
1701 match with_uarg {
1703 true => Some(t_next.take().unwrap().take_str()),
1704 false => None,
1705 }
1706 }
1707 _ => None,
1708 }
1709 } else {
1710 None
1711 }
1712 })
1713 .collect()
1714 }
1715
1716 fn next_uarg(&mut self) -> Option<String> {
1720 if let Some(p) = self.tokens.iter_mut().find(|s| match s {
1721 Some(Token::UnattachedArgument(_, _)) | Some(Token::Terminator(_)) => true,
1722 _ => false,
1723 }) {
1724 if let Some(Token::Terminator(_)) = p {
1725 None
1726 } else {
1727 Some(p.take().unwrap().take_str())
1728 }
1729 } else {
1730 None
1731 }
1732 }
1733
1734 fn is_help_enabled(&self) -> bool {
1736 self.help.is_some()
1738 }
1739
1740 fn try_to_help(&self) -> Result<()> {
1743 if self.options.prioritize_help == true
1744 && self.asking_for_help == true
1745 && self.is_help_enabled() == true
1746 {
1747 Err(Error::new(
1748 self.help.clone(),
1749 ErrorKind::Help,
1750 ErrorContext::Help,
1751 self.options.cap_mode,
1752 ))
1753 } else {
1754 Ok(())
1755 }
1756 }
1757}
1758
1759#[cfg(test)]
1760mod test {
1761 use super::*;
1762 use crate::error::ErrorKind;
1763
1764 fn args<'a>(args: Vec<&'a str>) -> Box<dyn Iterator<Item = String> + 'a> {
1766 Box::new(args.into_iter().map(|f| f.to_string()).into_iter())
1767 }
1768
1769 #[test]
1770 fn get_all_optionals() {
1771 let mut cli = Cli::new()
1773 .parse(args(vec![
1774 "orbit",
1775 "plan",
1776 "--fileset",
1777 "a",
1778 "--fileset=b",
1779 "--fileset",
1780 "c",
1781 ]))
1782 .save();
1783 let sets: Vec<String> = cli
1784 .get_option_all(Optional::new("fileset"))
1785 .unwrap()
1786 .unwrap();
1787 assert_eq!(sets, vec!["a", "b", "c"]);
1788 let mut cli = Cli::new()
1790 .parse(args(vec![
1791 "orbit",
1792 "plan",
1793 "--digit",
1794 "10",
1795 "--digit=9",
1796 "--digit",
1797 "c",
1798 ]))
1799 .save();
1800 assert_eq!(
1801 cli.get_option_all::<i32>(Optional::new("digit")).is_err(),
1802 true
1803 ); let mut cli = Cli::new()
1806 .parse(args(vec![
1807 "orbit",
1808 "plan",
1809 "--digit",
1810 "10",
1811 "--digit=9",
1812 "--digit",
1813 "1",
1814 ]))
1815 .save();
1816 let sets: Vec<i32> = cli.get_option_all(Optional::new("digit")).unwrap().unwrap();
1817 assert_eq!(sets, vec![10, 9, 1]);
1818 let mut cli = Cli::new()
1820 .parse(args(vec!["orbit", "plan", "--fileset", "a"]))
1821 .save();
1822 let sets: Vec<String> = cli
1823 .get_option_all(Optional::new("fileset"))
1824 .unwrap()
1825 .unwrap();
1826 assert_eq!(sets, vec!["a"]);
1827 let mut cli = Cli::new().parse(args(vec!["orbit", "plan"])).save();
1829 let sets: Option<Vec<String>> = cli.get_option_all(Optional::new("fileset")).unwrap();
1830 assert_eq!(sets, None);
1831 }
1832
1833 #[test]
1834 fn match_command() {
1835 let mut cli = Cli::new()
1836 .parse(args(vec![
1837 "orbit",
1838 "get",
1839 "rary.gates",
1840 "--instance",
1841 "--component",
1842 ]))
1843 .save();
1844 assert_eq!(
1846 cli.select(&["new", "get", "install", "edit"]).unwrap(),
1847 "get".to_string()
1848 );
1849
1850 let mut cli = Cli::new()
1851 .threshold(4)
1852 .parse(args(vec![
1853 "orbit",
1854 "got",
1855 "rary.gates",
1856 "--instance",
1857 "--component",
1858 ]))
1859 .save();
1860 assert!(cli.select(&["new", "get", "install", "edit"]).is_err());
1862 }
1863
1864 #[test]
1865 #[should_panic = "requires positional argument"]
1866 fn match_command_no_arg() {
1867 let mut cli = Cli::new()
1868 .parse(args(vec![
1869 "orbit",
1870 "got",
1871 "rary.gates",
1872 "--instance",
1873 "--component",
1874 ]))
1875 .save();
1876 assert!(cli.select(&["new", "get", "install", "edit"]).is_err());
1878 }
1879
1880 #[test]
1881 fn find_first_flag_left() {
1882 let cli = Cli::new()
1883 .parse(args(vec![
1884 "orbit",
1885 "--help",
1886 "new",
1887 "rary.gates",
1888 "--vcs",
1889 "git",
1890 ]))
1891 .save();
1892 assert_eq!(
1893 cli.find_first_flag_left(cli.tokens.len()),
1894 Some(("help", 0))
1895 );
1896
1897 let cli = Cli::new()
1898 .parse(args(vec!["orbit", "new", "rary.gates"]))
1899 .save();
1900 assert_eq!(cli.find_first_flag_left(cli.tokens.len()), None);
1901
1902 let cli = Cli::new()
1903 .parse(args(vec![
1904 "orbit",
1905 "new",
1906 "rary.gates",
1907 "--vcs",
1908 "git",
1909 "--help",
1910 ]))
1911 .save();
1912 assert_eq!(cli.find_first_flag_left(cli.tokens.len()), Some(("vcs", 2)));
1913
1914 let cli = Cli::new()
1915 .parse(args(vec!["orbit", "new", "rary.gates", "-c=git", "--help"]))
1916 .save();
1917 assert_eq!(cli.find_first_flag_left(cli.tokens.len()), Some(("c", 2)));
1918
1919 let cli = Cli::new()
1920 .parse(args(vec!["orbit", "new", "rary.gates", "-c=git", "--help"]))
1921 .save();
1922 assert_eq!(cli.find_first_flag_left(1), None); let cli = Cli::new()
1925 .parse(args(vec![
1926 "orbit",
1927 "--unknown",
1928 "new",
1929 "rary.gates",
1930 "-c=git",
1931 "--help",
1932 ]))
1933 .save();
1934 assert_eq!(cli.find_first_flag_left(1), Some(("unknown", 0))); }
1936
1937 #[test]
1938 fn processed_all_args() {
1939 let mut cli = Cli::new()
1940 .parse(args(vec![
1941 "orbit",
1942 "--upgrade",
1943 "new",
1944 "rary.gates",
1945 "--vcs",
1946 "git",
1947 ]))
1948 .save();
1949 let _ = cli.check_flag(Flag::new("upgrade")).unwrap();
1951 let _: Option<String> = cli.get_option(Optional::new("vcs")).unwrap();
1952 let _: String = cli.require_positional(Positional::new("command")).unwrap();
1953 let _: String = cli.require_positional(Positional::new("ip")).unwrap();
1954 assert_eq!(cli.empty().unwrap(), ());
1956
1957 let mut cli = Cli::new()
1958 .parse(args(vec!["orbit", "new", "rary.gates", symbol::FLAG]))
1959 .save();
1960 let _ = cli.check_flag(Flag::new("help")).unwrap();
1962 let _: Option<String> = cli.get_option(Optional::new("vcs")).unwrap();
1963 let _: String = cli.require_positional(Positional::new("command")).unwrap();
1964 let _: String = cli.require_positional(Positional::new("ip")).unwrap();
1965 assert!(cli.empty().is_err());
1967
1968 let mut cli = Cli::new()
1969 .parse(args(vec![
1970 "orbit",
1971 "--help",
1972 "new",
1973 "rary.gates",
1974 "--vcs",
1975 "git",
1976 ]))
1977 .save();
1978 assert!(cli.empty().is_err());
1980
1981 let mut cli = Cli::new()
1982 .parse(args(vec!["orbit", symbol::FLAG, "some", "extra", "words"]))
1983 .save();
1984 let _: Vec<String> = cli.remainder().unwrap();
1985 assert_eq!(cli.empty().unwrap(), ());
1987 }
1988
1989 #[test]
1990 fn tokenizer() {
1991 let cli = Cli::new().parse(args(vec![])).save();
1992 assert_eq!(cli.tokens, vec![]);
1993
1994 let cli = Cli::new().parse(args(vec!["orbit"])).save();
1995 assert_eq!(cli.tokens, vec![]);
1996
1997 let cli = Cli::new().parse(args(vec!["orbit", "--help"])).save();
1998 assert_eq!(cli.tokens, vec![Some(Token::Flag(0))]);
1999
2000 let cli = Cli::new().parse(args(vec!["orbit", "--help", "-v"])).save();
2001 assert_eq!(
2002 cli.tokens,
2003 vec![Some(Token::Flag(0)), Some(Token::Switch(1, 'v'))],
2004 );
2005
2006 let cli = Cli::new()
2007 .parse(args(vec!["orbit", "new", "rary.gates"]))
2008 .save();
2009 assert_eq!(
2010 cli.tokens,
2011 vec![
2012 Some(Token::UnattachedArgument(0, "new".to_string())),
2013 Some(Token::UnattachedArgument(1, "rary.gates".to_string())),
2014 ],
2015 );
2016
2017 let cli = Cli::new()
2018 .parse(args(vec!["orbit", "--help", "-vh"]))
2019 .save();
2020 assert_eq!(
2021 cli.tokens,
2022 vec![
2023 Some(Token::Flag(0)),
2024 Some(Token::Switch(1, 'v')),
2025 Some(Token::Switch(1, 'h')),
2026 ],
2027 );
2028
2029 let cli = Cli::new()
2030 .parse(args(vec!["orbit", "--help", "-vhc=10"]))
2031 .save();
2032 assert_eq!(
2033 cli.tokens,
2034 vec![
2035 Some(Token::Flag(0)),
2036 Some(Token::Switch(1, 'v')),
2037 Some(Token::Switch(1, 'h')),
2038 Some(Token::Switch(1, 'c')),
2039 Some(Token::AttachedArgument(1, "10".to_string())),
2040 ],
2041 );
2042
2043 let cli = Cli::new()
2045 .parse(args(vec!["orbit", "--=value", "extra"]))
2046 .save();
2047 assert_eq!(
2048 cli.tokens,
2049 vec![
2050 Some(Token::Terminator(0)),
2051 Some(Token::AttachedArgument(0, "value".to_string())),
2052 Some(Token::Ignore(1, "extra".to_string())),
2053 ]
2054 );
2055
2056 let cli = Cli::new().parse(args(vec![
2058 "orbit",
2059 "--help",
2060 "-v",
2061 "new",
2062 "ip",
2063 "--lib",
2064 "--name=rary.gates",
2065 "--help",
2066 "-sci",
2067 symbol::FLAG,
2068 "--map",
2069 "synthesis",
2070 "-jto",
2071 ]));
2072 assert_eq!(
2073 cli.tokens,
2074 vec![
2075 Some(Token::Flag(0)),
2076 Some(Token::Switch(1, 'v')),
2077 Some(Token::UnattachedArgument(2, "new".to_string())),
2078 Some(Token::UnattachedArgument(3, "ip".to_string())),
2079 Some(Token::Flag(4)),
2080 Some(Token::Flag(5)),
2081 Some(Token::AttachedArgument(5, "rary.gates".to_string())),
2082 Some(Token::Flag(6)),
2083 Some(Token::Switch(7, 's')),
2084 Some(Token::Switch(7, 'c')),
2085 Some(Token::Switch(7, 'i')),
2086 Some(Token::Terminator(8)),
2087 Some(Token::Ignore(9, "--map".to_string())),
2088 Some(Token::Ignore(10, "synthesis".to_string())),
2089 Some(Token::Ignore(11, "-jto".to_string())),
2090 ],
2091 );
2092 }
2093
2094 #[test]
2095 fn find_flags_and_switches() {
2096 let mut cli = Cli::new()
2097 .parse(args(vec![
2098 "orbit",
2099 "--help",
2100 "-v",
2101 "new",
2102 "ip",
2103 "--lib",
2104 "--name=rary.gates",
2105 "--help",
2106 "-sci",
2107 "-i",
2108 "--",
2109 "--map",
2110 "synthesis",
2111 "-jto",
2112 ]))
2113 .save();
2114
2115 assert_eq!(cli.take_flag_locs("version"), vec![]);
2117 assert_eq!(cli.take_flag_locs("lib"), vec![4]);
2119 assert_eq!(cli.take_flag_locs("help"), vec![0, 7]);
2121 assert_eq!(cli.take_flag_locs("map"), vec![]);
2123 assert_eq!(cli.take_flag_locs("rary.gates"), vec![]);
2125
2126 assert_eq!(cli.take_switch_locs(&'q'), vec![]);
2128 assert_eq!(cli.take_switch_locs(&'v'), vec![1]);
2130 assert_eq!(cli.take_switch_locs(&'i'), vec![10, 11]);
2132 assert_eq!(cli.take_switch_locs(&'j'), vec![]);
2134 }
2135
2136 #[test]
2137 fn flags_in_map() {
2138 let cli = Cli::new().parse(args(vec![
2139 "orbit",
2140 "--help",
2141 "-v",
2142 "new",
2143 "ip",
2144 "--lib",
2145 "--name=rary.gates",
2146 "--help",
2147 "-sci",
2148 symbol::FLAG,
2149 "--map",
2150 "synthesis",
2151 "-jto",
2152 ]));
2153 let mut store = HashMap::<Tag<String>, Slot>::new();
2154 store.insert(
2156 Tag::Flag("help".to_string()),
2157 Slot {
2158 pointers: vec![0, 7],
2159 visited: false,
2160 },
2161 );
2162 store.insert(
2163 Tag::Flag("lib".to_string()),
2164 Slot {
2165 pointers: vec![4],
2166 visited: false,
2167 },
2168 );
2169 store.insert(
2170 Tag::Flag("name".to_string()),
2171 Slot {
2172 pointers: vec![5],
2173 visited: false,
2174 },
2175 );
2176 store.insert(
2178 Tag::Switch("v".to_string()),
2179 Slot {
2180 pointers: vec![1],
2181 visited: false,
2182 },
2183 );
2184 store.insert(
2185 Tag::Switch("s".to_string()),
2186 Slot {
2187 pointers: vec![8],
2188 visited: false,
2189 },
2190 );
2191 store.insert(
2192 Tag::Switch("c".to_string()),
2193 Slot {
2194 pointers: vec![9],
2195 visited: false,
2196 },
2197 );
2198 store.insert(
2199 Tag::Switch("i".to_string()),
2200 Slot {
2201 pointers: vec![10],
2202 visited: false,
2203 },
2204 );
2205 assert_eq!(cli.store, store);
2206 }
2207
2208 #[test]
2209 fn take_unattached_args() {
2210 let mut cli = Cli::new()
2211 .parse(args(vec![
2212 "orbit",
2213 "--help",
2214 "-v",
2215 "new",
2216 "ip",
2217 "--lib",
2218 "--name=rary.gates",
2219 "--help",
2220 "-scii",
2221 "get",
2222 symbol::FLAG,
2223 "--map",
2224 "synthesis",
2225 "-jto",
2226 ]))
2227 .save();
2228
2229 assert_eq!(cli.next_uarg().unwrap(), "new".to_string());
2230 assert_eq!(cli.next_uarg().unwrap(), "ip".to_string());
2231 assert_eq!(cli.next_uarg().unwrap(), "get".to_string());
2232 assert_eq!(cli.next_uarg(), None);
2233 }
2234
2235 #[test]
2236 fn take_remainder_args() {
2237 let mut cli = Cli::new()
2238 .parse(args(vec![
2239 "orbit",
2240 "--help",
2241 "-v",
2242 "new",
2243 "ip",
2244 "--lib",
2245 "--name=rary.gates",
2246 "--help",
2247 "-scii",
2248 "get",
2249 symbol::FLAG,
2250 "--map",
2251 "synthesis",
2252 "-jto",
2253 ]))
2254 .save();
2255 assert_eq!(cli.remainder().unwrap(), vec!["--map", "synthesis", "-jto"]);
2256 assert_eq!(cli.remainder().unwrap(), Vec::<String>::new());
2258
2259 let mut cli = Cli::new()
2261 .parse(args(vec!["orbit", "--=value", "extra"]))
2262 .save();
2263 assert!(cli.remainder().is_err());
2264
2265 let mut cli = Cli::new().parse(args(vec!["orbit", "--help"])).save();
2266 assert_eq!(cli.remainder().unwrap(), Vec::<String>::new());
2268 }
2269
2270 #[test]
2271 fn pull_values_from_flags() {
2272 let mut cli = Cli::new().parse(args(vec!["orbit", "--help"])).save();
2273 let locs = cli.take_flag_locs("help");
2274 assert_eq!(cli.pull_flag(locs, false), vec![None]);
2275 assert_eq!(cli.tokens.get(0), Some(&None));
2276
2277 let mut cli = Cli::new()
2278 .parse(args(vec![
2279 "orbit",
2280 "--name",
2281 "gates",
2282 "arg",
2283 "--lib",
2284 "new",
2285 "--name=gates2",
2286 "--opt=1",
2287 "--opt",
2288 "--help",
2289 ]))
2290 .save();
2291 let locs = cli.take_flag_locs("lib");
2292 assert_eq!(cli.pull_flag(locs, false), vec![None]);
2293 assert_eq!(cli.tokens.get(3), Some(&None));
2295
2296 let locs = cli.take_flag_locs("name");
2298 assert_eq!(
2299 cli.pull_flag(locs, true),
2300 vec![Some("gates".to_string()), Some("gates2".to_string())]
2301 );
2302 assert_eq!(cli.tokens.get(0), Some(&None));
2303 assert_eq!(cli.tokens.get(5), Some(&None));
2304
2305 let locs = cli.take_flag_locs("opt");
2306 assert_eq!(cli.pull_flag(locs, true), vec![Some("1".to_string()), None]);
2307
2308 let mut cli = Cli::new()
2310 .parse(args(vec![
2311 "orbit",
2312 "--name",
2313 "gates",
2314 "-sicn",
2315 "dut",
2316 "new",
2317 "-vl=direct",
2318 "--help",
2319 "-l",
2320 "-m",
2321 "install",
2322 ]))
2323 .save();
2324 let locs = cli.take_switch_locs(&'l');
2325 assert_eq!(
2326 cli.pull_flag(locs, true),
2327 vec![Some("direct".to_string()), None]
2328 );
2329 assert_eq!(cli.tokens.get(9), Some(&None));
2330 assert_eq!(cli.tokens.get(12), Some(&None));
2331 let locs = cli.take_switch_locs(&'s');
2332 assert_eq!(cli.pull_flag(locs, true), vec![None]);
2333 let locs = cli.take_switch_locs(&'v');
2334 assert_eq!(cli.pull_flag(locs, true), vec![None]);
2335 let locs = cli.take_switch_locs(&'i');
2336 assert_eq!(cli.pull_flag(locs, true), vec![None]);
2337 let locs = cli.take_switch_locs(&'c');
2338 assert_eq!(cli.pull_flag(locs, false), vec![None]);
2339 let locs = cli.take_switch_locs(&'m');
2340 assert_eq!(cli.pull_flag(locs, false), vec![None]);
2341 }
2342
2343 #[test]
2344 fn check_flag() {
2345 let mut cli = Cli::new()
2346 .parse(args(vec!["orbit", "--help", "--verbose", "get"]))
2347 .save();
2348 assert_eq!(cli.check_flag(Flag::new("help")).unwrap(), true);
2349 assert_eq!(cli.check_flag(Flag::new("verbose")).unwrap(), true);
2350 assert_eq!(cli.check_flag(Flag::new("version")).unwrap(), false);
2351
2352 let mut cli = Cli::new()
2353 .parse(args(vec!["orbit", "--upgrade", "-u"]))
2354 .save();
2355 assert_eq!(
2356 cli.check_flag(Flag::new("upgrade").switch('u'))
2357 .unwrap_err()
2358 .kind(),
2359 ErrorKind::DuplicateOptions
2360 );
2361
2362 let mut cli = Cli::new()
2363 .parse(args(vec!["orbit", "--verbose", "--verbose", "--version=9"]))
2364 .save();
2365 assert_eq!(
2366 cli.check_flag(Flag::new("verbose")).unwrap_err().kind(),
2367 ErrorKind::DuplicateOptions
2368 );
2369 assert_eq!(
2370 cli.check_flag(Flag::new("version")).unwrap_err().kind(),
2371 ErrorKind::UnexpectedValue
2372 );
2373 }
2374
2375 #[test]
2376 fn check_positional() {
2377 let mut cli = Cli::new()
2378 .parse(args(vec!["orbit", "new", "rary.gates"]))
2379 .save();
2380 assert_eq!(
2381 cli.get_positional::<String>(Positional::new("command"))
2382 .unwrap(),
2383 Some("new".to_string())
2384 );
2385 assert_eq!(
2386 cli.get_positional::<String>(Positional::new("ip")).unwrap(),
2387 Some("rary.gates".to_string())
2388 );
2389 assert_eq!(
2390 cli.get_positional::<i32>(Positional::new("path")).unwrap(),
2391 None
2392 );
2393 }
2394
2395 #[test]
2396 fn check_option() {
2397 let mut cli = Cli::new()
2398 .parse(args(vec!["orbit", "command", "--rate", "10"]))
2399 .save();
2400 assert_eq!(cli.get_option(Optional::new("rate")).unwrap(), Some(10));
2401
2402 let mut cli = Cli::new()
2403 .parse(args(vec![
2404 "orbit", "--flag", "--rate=9", "command", "-r", "14",
2405 ]))
2406 .save();
2407 assert_eq!(
2408 cli.get_option::<i32>(Optional::new("rate").switch('r'))
2409 .unwrap_err()
2410 .kind(),
2411 ErrorKind::DuplicateOptions
2412 );
2413
2414 let mut cli = Cli::new()
2415 .parse(args(vec!["orbit", "--flag", "-r", "14"]))
2416 .save();
2417 assert_eq!(
2418 cli.get_option(Optional::new("rate").switch('r')).unwrap(),
2419 Some(14)
2420 );
2421
2422 let mut cli = Cli::new()
2423 .parse(args(vec!["orbit", "--flag", "--rate", "--verbose"]))
2424 .save();
2425 assert_eq!(
2426 cli.get_option::<i32>(Optional::new("rate"))
2427 .unwrap_err()
2428 .kind(),
2429 ErrorKind::ExpectingValue
2430 );
2431
2432 let mut cli = Cli::new()
2433 .parse(args(vec!["orbit", "--flag", "--rate", "five", "--verbose"]))
2434 .save();
2435 assert!(cli.get_option::<i32>(Optional::new("rate")).is_err());
2436 }
2437
2438 #[test]
2439 fn take_token_str() {
2440 let t = Token::UnattachedArgument(0, "get".to_string());
2441 assert_eq!(t.take_str(), "get");
2443
2444 let t = Token::AttachedArgument(1, "rary.gates".to_string());
2445 assert_eq!(t.take_str(), "rary.gates");
2446
2447 let t = Token::Ignore(7, "--map".to_string());
2448 assert_eq!(t.take_str(), "--map");
2449 }
2450
2451 #[test]
2452 #[should_panic]
2453 fn take_impossible_token_flag_str() {
2454 let t = Token::Flag(7);
2455 t.take_str();
2456 }
2457
2458 #[test]
2459 #[should_panic]
2460 fn take_impossible_token_switch_str() {
2461 let t = Token::Switch(7, 'h');
2462 t.take_str();
2463 }
2464
2465 #[test]
2466 #[should_panic]
2467 fn take_impossible_token_terminator_str() {
2468 let t = Token::Terminator(9);
2469 t.take_str();
2470 }
2471
2472 #[test]
2473 fn try_help_fail() {
2474 let mut cli = Cli::new().parse(args(vec!["orbit", "--h"])).save();
2475 let locs = cli.take_flag_locs("help");
2476 assert_eq!(locs.len(), 0);
2477 assert_eq!(cli.pull_flag(locs, false), vec![]);
2478 }
2479
2480 #[test]
2481 fn check_option_n() {
2482 let mut cli = Cli::new()
2483 .parse(args(vec!["orbit", "command", "--rate", "10"]))
2484 .save();
2485 assert_eq!(
2486 cli.get_option_until(Optional::new("rate"), 1).unwrap(),
2487 Some(vec![10])
2488 );
2489
2490 let mut cli = Cli::new()
2491 .parse(args(vec!["orbit", "command", "--rate", "10"]))
2492 .save();
2493 assert_eq!(
2494 cli.get_option_until(Optional::new("rate"), 2).unwrap(),
2495 Some(vec![10])
2496 );
2497
2498 let mut cli = Cli::new()
2499 .parse(args(vec![
2500 "orbit", "command", "--rate", "10", "--rate", "4", "--rate", "3",
2501 ]))
2502 .save();
2503 assert_eq!(
2504 cli.get_option_until::<u8>(Optional::new("rate"), 2)
2505 .is_err(),
2506 true
2507 );
2508 }
2509
2510 #[test]
2511 fn check_flag_n() {
2512 let mut cli = Cli::new().parse(args(vec!["orbit"])).save();
2513 assert_eq!(cli.check_flag_until(Flag::new("debug"), 4).unwrap(), 0);
2514
2515 let mut cli = Cli::new().parse(args(vec!["orbit", "--debug"])).save();
2516 assert_eq!(cli.check_flag_until(Flag::new("debug"), 1).unwrap(), 1);
2517
2518 let mut cli = Cli::new()
2519 .parse(args(vec!["orbit", "--debug", "--debug"]))
2520 .save();
2521 assert_eq!(cli.check_flag_until(Flag::new("debug"), 3).unwrap(), 2);
2522
2523 let mut cli = Cli::new()
2524 .parse(args(vec!["orbit", "--debug", "--debug", "--debug"]))
2525 .save();
2526 assert_eq!(cli.check_flag_until(Flag::new("debug"), 3).unwrap(), 3);
2527
2528 let mut cli = Cli::new()
2529 .parse(args(vec![
2530 "orbit", "--debug", "--debug", "--debug", "--debug",
2531 ]))
2532 .save();
2533 assert_eq!(cli.check_flag_until(Flag::new("debug"), 3).is_err(), true);
2534 }
2535
2536 #[test]
2537 fn check_flag_all() {
2538 let mut cli = Cli::new().parse(args(vec!["orbit"])).save();
2539 assert_eq!(cli.check_flag_all(Flag::new("debug")).unwrap(), 0);
2540
2541 let mut cli = Cli::new()
2542 .parse(args(vec!["orbit", "--debug", "--debug", "--debug"]))
2543 .save();
2544 assert_eq!(cli.check_flag_all(Flag::new("debug")).unwrap(), 3);
2545
2546 let mut cli = Cli::new()
2547 .parse(args(vec!["orbit", "--debug", "--debug", "--debug=1"]))
2548 .save();
2549 assert_eq!(cli.check_flag_all(Flag::new("debug")).is_err(), true);
2550 }
2551
2552 #[test]
2553 fn requires_positional_all() {
2554 let mut cli = Cli::new().parse(args(vec!["sum", "10", "20", "30"])).save();
2555 assert_eq!(
2556 cli.require_positional_all::<u8>(Positional::new("digit"))
2557 .unwrap(),
2558 vec![10, 20, 30]
2559 );
2560
2561 let mut cli = Cli::new().parse(args(vec!["sum"])).save();
2562 assert_eq!(
2563 cli.require_positional_all::<u8>(Positional::new("digit"))
2564 .unwrap_err()
2565 .kind(),
2566 ErrorKind::MissingPositional
2567 );
2568
2569 let mut cli = Cli::new()
2570 .parse(args(vec!["sum", "10", "--option", "20", "30"]))
2571 .save();
2572 assert_eq!(
2574 cli.get_option::<u8>(Optional::new("option")).unwrap(),
2575 Some(20)
2576 );
2577 assert_eq!(
2578 cli.require_positional_all::<u8>(Positional::new("digit"))
2579 .unwrap(),
2580 vec![10, 30]
2581 );
2582
2583 let mut cli = Cli::new().parse(args(vec!["sum", "100"])).save();
2584 assert_eq!(
2585 cli.require_positional_all::<u8>(Positional::new("digit"))
2586 .unwrap(),
2587 vec![100]
2588 );
2589 }
2590
2591 #[test]
2592 fn is_empty_from_parsing() {
2593 let cli = Cli::new().parse(args(vec!["cp"])).save();
2594 assert_eq!(cli.is_empty(), true);
2595
2596 let cli = Cli::new().parse(args(vec!["cp", "echo"])).save();
2597 assert_eq!(cli.is_empty(), false);
2598
2599 let cli = Cli::new().parse(args(vec!["cp", "--", "hello"])).save();
2600 assert_eq!(cli.is_empty(), false);
2601 }
2602}