@@ -6,7 +6,7 @@ use super::parse_args;
6
6
use crate :: errors:: InvalidOption ;
7
7
use crate :: Cmd ;
8
8
9
- impl < ' a > Cmd < ' a > {
9
+ impl < ' b , ' a > Cmd < ' a > {
10
10
/// Parses command line arguments without configurations.
11
11
///
12
12
/// This method divides command line arguments into options and command arguments based on
@@ -28,6 +28,7 @@ impl<'a> Cmd<'a> {
28
28
/// use cliargs::errors::InvalidOption;
29
29
///
30
30
/// let mut cmd = Cmd::with_strings(vec![ /* ... */ ]);
31
+ ///
31
32
/// match cmd.parse() {
32
33
/// Ok(_) => { /* ... */ },
33
34
/// Err(InvalidOption::OptionContainsInvalidChar { option }) => {
@@ -51,9 +52,9 @@ impl<'a> Cmd<'a> {
51
52
52
53
let take_opt_args = |_arg : & str | false ;
53
54
54
- if ! self . _leaked_strs . is_empty ( ) {
55
+ if self . _num_of_args > 0 {
55
56
match parse_args (
56
- & self . _leaked_strs [ 1 ..] ,
57
+ & self . _leaked_strs [ 1 ..( self . _num_of_args ) ] ,
57
58
collect_args,
58
59
collect_opts,
59
60
take_opt_args,
@@ -66,6 +67,64 @@ impl<'a> Cmd<'a> {
66
67
67
68
Ok ( ( ) )
68
69
}
70
+
71
+ /// Parses command line arguments without configurations but stops parsing when encountering
72
+ /// first command argument.
73
+ ///
74
+ /// This method creates and returns a new [Cmd] instance that holds the command line arguments
75
+ /// starting from the first command argument.
76
+ ///
77
+ /// This method parses command line arguments in the same way as the [Cmd::parse] method, except
78
+ /// that it only parses the command line arguments before the first command argument.
79
+ ///
80
+ /// ```rust
81
+ /// use cliargs::Cmd;
82
+ /// use cliargs::errors::InvalidOption;
83
+ ///
84
+ /// let mut cmd = Cmd::with_strings(vec![ /* ... */ ]);
85
+ ///
86
+ /// match cmd.parse_until_sub_cmd() {
87
+ /// Ok(Some(mut sub_cmd)) => {
88
+ /// let sub_cmd_name = sub_cmd.name();
89
+ /// match sub_cmd.parse() {
90
+ /// Ok(_) => { /* ... */ },
91
+ /// Err(err) => panic!("Invalid option: {}", err.option()),
92
+ /// }
93
+ /// },
94
+ /// Ok(None) => { /* ... */ },
95
+ /// Err(InvalidOption::OptionContainsInvalidChar { option }) => {
96
+ /// panic!("Option contains invalid character: {option}");
97
+ /// },
98
+ /// Err(err) => panic!("Invalid option: {}", err.option()),
99
+ /// }
100
+ /// ```
101
+ pub fn parse_until_sub_cmd ( & mut self ) -> Result < Option < Cmd < ' b > > , InvalidOption > {
102
+ let collect_args = |_arg| { } ;
103
+
104
+ let collect_opts = |name, option| {
105
+ let vec = self . opts . entry ( name) . or_insert_with ( || Vec :: new ( ) ) ;
106
+ if let Some ( arg) = option {
107
+ vec. push ( arg) ;
108
+ }
109
+ Ok ( ( ) )
110
+ } ;
111
+
112
+ let take_opt_args = |_arg : & str | false ;
113
+
114
+ if self . _num_of_args > 0 {
115
+ if let Some ( idx) = parse_args (
116
+ & self . _leaked_strs [ 1 ..( self . _num_of_args ) ] ,
117
+ collect_args,
118
+ collect_opts,
119
+ take_opt_args,
120
+ true ,
121
+ ) ? {
122
+ return Ok ( Some ( self . sub_cmd ( idx + 1 ) ) ) ; // +1, because parse_args parses from 1.
123
+ }
124
+ }
125
+
126
+ Ok ( None )
127
+ }
69
128
}
70
129
71
130
#[ cfg( test) ]
@@ -572,3 +631,172 @@ mod tests_of_cmd {
572
631
}
573
632
}
574
633
}
634
+
635
+ #[ cfg( test) ]
636
+ mod tests_of_parse_until_sub_cmd {
637
+ use super :: * ;
638
+
639
+ #[ test]
640
+ fn test_if_command_line_arguments_contains_no_command_argument_and_option ( ) {
641
+ let ui_args = vec ! [ "/path/to/app" . to_string( ) ] ;
642
+ let mut cmd = Cmd :: with_strings ( ui_args) ;
643
+
644
+ match cmd. parse_until_sub_cmd ( ) {
645
+ Ok ( None ) => { }
646
+ Ok ( Some ( _) ) => assert ! ( false ) ,
647
+ Err ( _) => assert ! ( false ) ,
648
+ }
649
+
650
+ assert_eq ! ( cmd. name( ) , "app" ) ;
651
+ assert_eq ! ( cmd. args( ) , & [ ] as & [ & str ] ) ;
652
+ }
653
+
654
+ #[ test]
655
+ fn test_if_command_line_arguments_contains_only_command_arguments ( ) {
656
+ let ui_args = vec ! [
657
+ "/path/to/app" . to_string( ) ,
658
+ "foo" . to_string( ) ,
659
+ "bar" . to_string( ) ,
660
+ ] ;
661
+ let mut cmd = Cmd :: with_strings ( ui_args) ;
662
+
663
+ match cmd. parse_until_sub_cmd ( ) {
664
+ Ok ( Some ( mut sub_cmd) ) => {
665
+ assert_eq ! ( sub_cmd. name( ) , "foo" ) ;
666
+ assert_eq ! ( sub_cmd. args( ) , & [ ] as & [ & str ] ) ;
667
+
668
+ match sub_cmd. parse ( ) {
669
+ Ok ( _) => { }
670
+ Err ( _) => assert ! ( false ) ,
671
+ }
672
+
673
+ assert_eq ! ( sub_cmd. name( ) , "foo" ) ;
674
+ assert_eq ! ( sub_cmd. args( ) , & [ "bar" ] ) ;
675
+ }
676
+ Ok ( None ) => assert ! ( false ) ,
677
+ Err ( _) => assert ! ( false ) ,
678
+ }
679
+
680
+ assert_eq ! ( cmd. name( ) , "app" ) ;
681
+ assert_eq ! ( cmd. args( ) , & [ ] as & [ & str ] ) ;
682
+
683
+ //
684
+
685
+ let f = || {
686
+ let ui_args = vec ! [
687
+ "/path/to/app" . to_string( ) ,
688
+ "foo" . to_string( ) ,
689
+ "bar" . to_string( ) ,
690
+ ] ;
691
+ let mut cmd = Cmd :: with_strings ( ui_args) ;
692
+
693
+ if let Some ( mut sub_cmd) = cmd. parse_until_sub_cmd ( ) ? {
694
+ assert_eq ! ( sub_cmd. name( ) , "foo" ) ;
695
+ assert_eq ! ( sub_cmd. args( ) , & [ ] as & [ & str ] ) ;
696
+
697
+ match sub_cmd. parse ( ) {
698
+ Ok ( _) => { }
699
+ Err ( _) => assert ! ( false ) ,
700
+ }
701
+
702
+ assert_eq ! ( sub_cmd. name( ) , "foo" ) ;
703
+ assert_eq ! ( sub_cmd. args( ) , & [ "bar" ] ) ;
704
+
705
+ assert_eq ! ( cmd. name( ) , "app" ) ;
706
+ assert_eq ! ( cmd. args( ) , & [ ] as & [ & str ] ) ;
707
+ } else {
708
+ assert_eq ! ( cmd. name( ) , "app" ) ;
709
+ assert_eq ! ( cmd. args( ) , & [ ] as & [ & str ] ) ;
710
+ }
711
+
712
+ Ok :: < ( ) , InvalidOption > ( ( ) )
713
+ } ;
714
+ let _ = f ( ) ;
715
+ }
716
+
717
+ #[ test]
718
+ fn test_if_command_line_arguments_contains_only_command_options ( ) {
719
+ let ui_args = vec ! [
720
+ "/path/to/app" . to_string( ) ,
721
+ "--foo" . to_string( ) ,
722
+ "-b" . to_string( ) ,
723
+ ] ;
724
+ let mut cmd = Cmd :: with_strings ( ui_args) ;
725
+
726
+ match cmd. parse_until_sub_cmd ( ) {
727
+ Ok ( None ) => { }
728
+ Ok ( Some ( _) ) => assert ! ( false ) ,
729
+ Err ( _) => assert ! ( false ) ,
730
+ }
731
+
732
+ assert_eq ! ( cmd. name( ) , "app" ) ;
733
+ assert_eq ! ( cmd. args( ) , & [ ] as & [ & str ] ) ;
734
+ assert_eq ! ( cmd. has_opt( "foo" ) , true ) ;
735
+ assert_eq ! ( cmd. has_opt( "b" ) , true ) ;
736
+ assert_eq ! ( cmd. opt_arg( "foo" ) , None ) ;
737
+ assert_eq ! ( cmd. opt_arg( "b" ) , None ) ;
738
+ }
739
+
740
+ #[ test]
741
+ fn test_if_command_line_arguments_contains_both_command_arguments_and_options ( ) {
742
+ let ui_args = vec ! [
743
+ "/path/to/app" . to_string( ) ,
744
+ "--foo=123" . to_string( ) ,
745
+ "bar" . to_string( ) ,
746
+ "--baz" . to_string( ) ,
747
+ "-q=ABC" . to_string( ) ,
748
+ "quux" . to_string( ) ,
749
+ ] ;
750
+ let mut cmd = Cmd :: with_strings ( ui_args) ;
751
+
752
+ if let Some ( mut sub_cmd) = cmd. parse_until_sub_cmd ( ) . unwrap ( ) {
753
+ assert_eq ! ( sub_cmd. name( ) , "bar" ) ;
754
+ assert_eq ! ( sub_cmd. args( ) , & [ ] as & [ & str ] ) ;
755
+ assert_eq ! ( cmd. has_opt( "baz" ) , false ) ;
756
+ assert_eq ! ( cmd. opt_arg( "baz" ) , None ) ;
757
+ assert_eq ! ( cmd. has_opt( "q" ) , false ) ;
758
+ assert_eq ! ( cmd. opt_arg( "q" ) , None ) ;
759
+
760
+ match sub_cmd. parse ( ) {
761
+ Ok ( _) => { }
762
+ Err ( _) => assert ! ( false ) ,
763
+ }
764
+
765
+ assert_eq ! ( sub_cmd. name( ) , "bar" ) ;
766
+ assert_eq ! ( sub_cmd. args( ) , & [ "quux" ] ) ;
767
+ assert_eq ! ( sub_cmd. has_opt( "baz" ) , true ) ;
768
+ assert_eq ! ( sub_cmd. opt_arg( "baz" ) , None ) ;
769
+ assert_eq ! ( sub_cmd. has_opt( "q" ) , true ) ;
770
+ assert_eq ! ( sub_cmd. opt_arg( "q" ) , Some ( "ABC" ) ) ;
771
+ }
772
+
773
+ assert_eq ! ( cmd. name( ) , "app" ) ;
774
+ assert_eq ! ( cmd. args( ) , & [ ] as & [ & str ] ) ;
775
+ assert_eq ! ( cmd. has_opt( "foo" ) , true ) ;
776
+ assert_eq ! ( cmd. opt_arg( "foo" ) , Some ( "123" ) ) ;
777
+ }
778
+
779
+ #[ test]
780
+ fn test_if_fail_to_parse ( ) {
781
+ let ui_args = vec ! [
782
+ "/path/to/app" . to_string( ) ,
783
+ "--f#o" . to_string( ) ,
784
+ "bar" . to_string( ) ,
785
+ ] ;
786
+ let mut cmd = Cmd :: with_strings ( ui_args) ;
787
+
788
+ match cmd. parse_until_sub_cmd ( ) {
789
+ Ok ( None ) => assert ! ( false ) ,
790
+ Ok ( Some ( _) ) => assert ! ( false ) ,
791
+ Err ( InvalidOption :: OptionContainsInvalidChar { option } ) => {
792
+ assert_eq ! ( option, "f#o" ) ;
793
+ }
794
+ Err ( _) => assert ! ( false ) ,
795
+ }
796
+
797
+ assert_eq ! ( cmd. name( ) , "app" ) ;
798
+ assert_eq ! ( cmd. args( ) , & [ ] as & [ & str ] ) ;
799
+ assert_eq ! ( cmd. has_opt( "f#o" ) , false ) ;
800
+ assert_eq ! ( cmd. opt_arg( "f#o" ) , None ) ;
801
+ }
802
+ }
0 commit comments