Skip to content

Commit 663a0f8

Browse files
committed
fix: modified to inherit end opt flag after parsing until sub command
1 parent 6e65a99 commit 663a0f8

File tree

5 files changed

+387
-16
lines changed

5 files changed

+387
-16
lines changed

src/lib.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ pub struct Cmd<'a> {
352352
args: Vec<&'a str>,
353353
opts: HashMap<&'a str, Vec<&'a str>>,
354354
cfgs: Vec<OptCfg>,
355+
is_after_end_opt: bool,
355356

356357
_leaked_strs: Vec<&'a str>,
357358
_num_of_args: usize,
@@ -457,6 +458,7 @@ impl<'b, 'a> Cmd<'a> {
457458
args: Vec::new(),
458459
opts: HashMap::new(),
459460
cfgs: Vec::new(),
461+
is_after_end_opt: false,
460462
_leaked_strs,
461463
_num_of_args,
462464
})
@@ -496,12 +498,13 @@ impl<'b, 'a> Cmd<'a> {
496498
args: Vec::new(),
497499
opts: HashMap::new(),
498500
cfgs: Vec::new(),
501+
is_after_end_opt: false,
499502
_leaked_strs,
500503
_num_of_args,
501504
}
502505
}
503506

504-
fn sub_cmd(&'a self, from_index: usize) -> Cmd<'b> {
507+
fn sub_cmd(&'a self, from_index: usize, is_after_end_opt: bool) -> Cmd<'b> {
505508
let arg_iter = self._leaked_strs[from_index..(self._num_of_args)].into_iter();
506509
let (size, _) = arg_iter.size_hint();
507510
let mut _leaked_strs = Vec::with_capacity(size);
@@ -518,6 +521,7 @@ impl<'b, 'a> Cmd<'a> {
518521
args: Vec::new(),
519522
opts: HashMap::new(),
520523
cfgs: Vec::new(),
524+
is_after_end_opt: is_after_end_opt,
521525
_leaked_strs,
522526
_num_of_args,
523527
}

src/parse/mod.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@ fn parse_args<'a, F1, F2, F3>(
1717
mut collect_opts: F2,
1818
take_opt_args: F3,
1919
until_1st_arg: bool,
20-
) -> Result<Option<usize>, InvalidOption>
20+
is_after_end_opt: bool,
21+
) -> Result<Option<(usize, bool)>, InvalidOption>
2122
where
2223
F1: FnMut(&'a str),
2324
F2: FnMut(&'a str, Option<&'a str>) -> Result<(), InvalidOption>,
2425
F3: Fn(&str) -> bool,
2526
{
26-
let mut is_non_opt = false;
27+
let mut is_non_opt = is_after_end_opt;
2728
let mut prev_opt_taking_args = "";
2829
let mut first_err: Option<InvalidOption> = None;
2930

@@ -33,7 +34,7 @@ where
3334
if let Some(err) = first_err {
3435
return Err(err);
3536
}
36-
return Ok(Some(i_arg));
37+
return Ok(Some((i_arg, is_non_opt)));
3738
}
3839
collect_args(arg);
3940
} else if !prev_opt_taking_args.is_empty() {
@@ -114,7 +115,7 @@ where
114115
if let Some(err) = first_err {
115116
return Err(err);
116117
}
117-
return Ok(Some(i_arg));
118+
return Ok(Some((i_arg, is_non_opt)));
118119
}
119120
collect_args(arg);
120121
continue 'L0;
@@ -183,7 +184,7 @@ where
183184
if let Some(err) = first_err {
184185
return Err(err);
185186
}
186-
return Ok(Some(i_arg));
187+
return Ok(Some((i_arg, is_non_opt)));
187188
}
188189
collect_args(arg);
189190
}

src/parse/parse.rs

+190-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ impl<'b, 'a> Cmd<'a> {
5959
collect_opts,
6060
take_opt_args,
6161
false,
62+
self.is_after_end_opt,
6263
) {
6364
Ok(_) => {}
6465
Err(err) => return Err(err),
@@ -112,14 +113,15 @@ impl<'b, 'a> Cmd<'a> {
112113
let take_opt_args = |_arg: &str| false;
113114

114115
if self._num_of_args > 0 {
115-
if let Some(idx) = parse_args(
116+
if let Some((idx, is_after_end_opt)) = parse_args(
116117
&self._leaked_strs[1..(self._num_of_args)],
117118
collect_args,
118119
collect_opts,
119120
take_opt_args,
120121
true,
122+
self.is_after_end_opt,
121123
)? {
122-
return Ok(Some(self.sub_cmd(idx + 1))); // +1, because parse_args parses from 1.
124+
return Ok(Some(self.sub_cmd(idx + 1, is_after_end_opt))); // +1, because parse_args parses from 1.
123125
}
124126
}
125127

@@ -823,4 +825,190 @@ mod tests_of_parse_until_sub_cmd {
823825
assert_eq!(sub_cmd.has_opt("baz"), true);
824826
}
825827
}
828+
829+
#[test]
830+
fn should_parse_single_hyphen() {
831+
let mut cmd = Cmd::with_strings([
832+
"/path/to/app".to_string(),
833+
"-a".to_string(),
834+
"-".to_string(),
835+
"b".to_string(),
836+
"-".to_string(),
837+
]);
838+
839+
match cmd.parse_until_sub_cmd() {
840+
Ok(None) => assert!(false),
841+
Ok(Some(mut sub_cmd)) => {
842+
assert_eq!(cmd.name(), "app");
843+
assert_eq!(cmd.args(), &[] as &[&str]);
844+
assert_eq!(cmd.has_opt("a"), true);
845+
846+
match sub_cmd.parse() {
847+
Ok(_) => {
848+
assert_eq!(sub_cmd.name(), "-");
849+
assert_eq!(sub_cmd.args(), &["b", "-"]);
850+
assert_eq!(sub_cmd.has_opt("a"), false);
851+
}
852+
Err(_) => assert!(false),
853+
}
854+
}
855+
Err(_) => assert!(false),
856+
}
857+
}
858+
859+
#[test]
860+
fn should_parse_single_hyphen_but_error() {
861+
let mut cmd = Cmd::with_strings([
862+
"/path/to/app".to_string(),
863+
"-a".to_string(),
864+
"-@".to_string(),
865+
"-".to_string(),
866+
"b".to_string(),
867+
"-".to_string(),
868+
]);
869+
870+
match cmd.parse_until_sub_cmd() {
871+
Ok(_) => assert!(false),
872+
Err(InvalidOption::OptionContainsInvalidChar { option }) => {
873+
assert_eq!(option, "@");
874+
}
875+
Err(_) => assert!(false),
876+
}
877+
878+
assert_eq!(cmd.name(), "app");
879+
assert_eq!(cmd.args(), &[] as &[&str]);
880+
assert_eq!(cmd.has_opt("a"), true);
881+
}
882+
883+
#[test]
884+
fn should_parse_with_end_opt_mark() {
885+
let mut cmd = Cmd::with_strings([
886+
"/path/to/app".to_string(),
887+
"sub".to_string(),
888+
"--".to_string(),
889+
"-a".to_string(),
890+
"-s@".to_string(),
891+
"--".to_string(),
892+
"xxx".to_string(),
893+
]);
894+
895+
match cmd.parse_until_sub_cmd() {
896+
Ok(None) => assert!(false),
897+
Ok(Some(mut sub_cmd)) => {
898+
assert_eq!(cmd.name(), "app");
899+
assert_eq!(cmd.args(), &[] as &[&str]);
900+
assert_eq!(cmd.has_opt("a"), false);
901+
assert_eq!(cmd.opt_arg("a"), None);
902+
assert_eq!(cmd.opt_args("a"), None);
903+
assert_eq!(cmd.has_opt("alphabet"), false);
904+
assert_eq!(cmd.opt_arg("alphabet"), None);
905+
assert_eq!(cmd.opt_args("alphabet"), None);
906+
assert_eq!(cmd.has_opt("s"), false);
907+
assert_eq!(cmd.opt_arg("s"), None);
908+
assert_eq!(cmd.opt_args("s"), None);
909+
assert_eq!(cmd.has_opt("silent"), false);
910+
assert_eq!(cmd.opt_arg("silent"), None);
911+
assert_eq!(cmd.opt_args("silent"), None);
912+
913+
match sub_cmd.parse() {
914+
Err(_) => assert!(false),
915+
Ok(_) => {
916+
assert_eq!(sub_cmd.name(), "sub");
917+
assert_eq!(sub_cmd.args(), &["-a", "-s@", "--", "xxx"]);
918+
assert_eq!(sub_cmd.has_opt("a"), false);
919+
assert_eq!(sub_cmd.opt_arg("a"), None);
920+
assert_eq!(sub_cmd.opt_args("a"), None);
921+
assert_eq!(sub_cmd.has_opt("alphabet"), false);
922+
assert_eq!(sub_cmd.opt_arg("alphabet"), None);
923+
assert_eq!(sub_cmd.opt_args("alphabet"), None);
924+
assert_eq!(sub_cmd.has_opt("s"), false);
925+
assert_eq!(sub_cmd.opt_arg("s"), None);
926+
assert_eq!(sub_cmd.opt_args("s"), None);
927+
assert_eq!(sub_cmd.has_opt("silent"), false);
928+
assert_eq!(sub_cmd.opt_arg("silent"), None);
929+
assert_eq!(sub_cmd.opt_args("silent"), None);
930+
}
931+
}
932+
}
933+
Err(_) => assert!(false),
934+
}
935+
}
936+
937+
#[test]
938+
fn should_parse_after_end_opt_mark() {
939+
let mut cmd = Cmd::with_strings([
940+
"/path/to/app".to_string(),
941+
"-s".to_string(),
942+
"--".to_string(),
943+
"-a".to_string(),
944+
"-s@".to_string(),
945+
"--".to_string(),
946+
"xxx".to_string(),
947+
]);
948+
949+
match cmd.parse_until_sub_cmd() {
950+
Ok(None) => assert!(false),
951+
Ok(Some(mut sub_cmd)) => {
952+
assert_eq!(cmd.name(), "app");
953+
assert_eq!(cmd.args(), &[] as &[&str]);
954+
assert_eq!(cmd.has_opt("a"), false);
955+
assert_eq!(cmd.opt_arg("a"), None);
956+
assert_eq!(cmd.opt_args("a"), None);
957+
assert_eq!(cmd.has_opt("alphabet"), false);
958+
assert_eq!(cmd.opt_arg("alphabet"), None);
959+
assert_eq!(cmd.opt_args("alphabet"), None);
960+
assert_eq!(cmd.has_opt("s"), true);
961+
assert_eq!(cmd.opt_arg("s"), None);
962+
assert_eq!(cmd.opt_args("s"), Some(&[] as &[&str]));
963+
assert_eq!(cmd.has_opt("silent"), false);
964+
assert_eq!(cmd.opt_arg("silent"), None);
965+
assert_eq!(cmd.opt_args("silent"), None);
966+
967+
match sub_cmd.parse() {
968+
Err(_) => assert!(false),
969+
Ok(_) => {
970+
assert_eq!(sub_cmd.name(), "-a");
971+
assert_eq!(sub_cmd.args(), &["-s@", "--", "xxx"]);
972+
assert_eq!(sub_cmd.has_opt("a"), false);
973+
assert_eq!(sub_cmd.opt_arg("a"), None);
974+
assert_eq!(sub_cmd.opt_args("a"), None);
975+
assert_eq!(sub_cmd.has_opt("alphabet"), false);
976+
assert_eq!(sub_cmd.opt_arg("alphabet"), None);
977+
assert_eq!(sub_cmd.opt_args("alphabet"), None);
978+
assert_eq!(sub_cmd.has_opt("s"), false);
979+
assert_eq!(sub_cmd.opt_arg("s"), None);
980+
assert_eq!(sub_cmd.opt_args("s"), None);
981+
assert_eq!(sub_cmd.has_opt("silent"), false);
982+
assert_eq!(sub_cmd.opt_arg("silent"), None);
983+
assert_eq!(sub_cmd.opt_args("silent"), None);
984+
}
985+
}
986+
}
987+
Err(_) => assert!(false),
988+
}
989+
}
990+
991+
#[test]
992+
fn should_parse_after_end_opt_mark_but_error() {
993+
let mut cmd = Cmd::with_strings([
994+
"/path/to/app".to_string(),
995+
"-@".to_string(),
996+
"--".to_string(),
997+
"-a".to_string(),
998+
"-s@".to_string(),
999+
"--".to_string(),
1000+
"xxx".to_string(),
1001+
]);
1002+
1003+
match cmd.parse_until_sub_cmd() {
1004+
Ok(_) => assert!(false),
1005+
Err(InvalidOption::OptionContainsInvalidChar { option }) => {
1006+
assert_eq!(option, "@");
1007+
}
1008+
Err(_) => assert!(false),
1009+
}
1010+
1011+
assert_eq!(cmd.name(), "app");
1012+
assert_eq!(cmd.args(), &[] as &[&str]);
1013+
}
8261014
}

src/parse/parse_for.rs

+88
Original file line numberDiff line numberDiff line change
@@ -1909,4 +1909,92 @@ mod tests_of_parse_util_sub_cmd_for {
19091909

19101910
assert_eq!(my_options.foo, false);
19111911
}
1912+
1913+
#[test]
1914+
fn should_parse_with_end_opt_mark() {
1915+
let mut my_options = MyOptions::with_defaults();
1916+
let mut sub_options = SubOptions::with_defaults();
1917+
1918+
let mut cmd = Cmd::with_strings([
1919+
"app".to_string(),
1920+
"--foo".to_string(),
1921+
"sub".to_string(),
1922+
"--".to_string(),
1923+
"bar".to_string(),
1924+
"-@".to_string(),
1925+
]);
1926+
1927+
match cmd.parse_until_sub_cmd_for(&mut my_options) {
1928+
Ok(Some(mut sub_cmd)) => {
1929+
assert_eq!(cmd.name(), "app");
1930+
assert_eq!(cmd.args(), &[] as &[&str]);
1931+
assert_eq!(cmd.has_opt("foo"), true);
1932+
assert_eq!(cmd.opt_arg("foo"), None);
1933+
assert_eq!(cmd.opt_args("foo"), Some(&[] as &[&str]));
1934+
assert_eq!(cmd.has_opt("bar"), false);
1935+
assert_eq!(cmd.opt_arg("bar"), None);
1936+
assert_eq!(cmd.opt_args("bar"), None);
1937+
1938+
match sub_cmd.parse_for(&mut sub_options) {
1939+
Ok(_) => {
1940+
assert_eq!(sub_cmd.name(), "sub");
1941+
assert_eq!(sub_cmd.args(), &["bar", "-@"] as &[&str]);
1942+
assert_eq!(sub_cmd.has_opt("foo"), false);
1943+
assert_eq!(sub_cmd.opt_arg("foo"), None);
1944+
assert_eq!(sub_cmd.opt_args("foo"), None);
1945+
assert_eq!(sub_cmd.has_opt("bar"), false);
1946+
assert_eq!(sub_cmd.opt_arg("bar"), None);
1947+
assert_eq!(sub_cmd.opt_args("bar"), None);
1948+
}
1949+
Err(_) => assert!(false),
1950+
}
1951+
}
1952+
Ok(None) => assert!(false),
1953+
Err(_) => assert!(false),
1954+
}
1955+
}
1956+
1957+
#[test]
1958+
fn should_parse_after_end_opt_mark() {
1959+
let mut my_options = MyOptions::with_defaults();
1960+
let mut sub_options = SubOptions::with_defaults();
1961+
1962+
let mut cmd = Cmd::with_strings([
1963+
"app".to_string(),
1964+
"--".to_string(),
1965+
"--foo".to_string(),
1966+
"sub".to_string(),
1967+
"bar".to_string(),
1968+
"-@".to_string(),
1969+
]);
1970+
1971+
match cmd.parse_until_sub_cmd_for(&mut my_options) {
1972+
Ok(Some(mut sub_cmd)) => {
1973+
assert_eq!(cmd.name(), "app");
1974+
assert_eq!(cmd.args(), &[] as &[&str]);
1975+
assert_eq!(cmd.has_opt("foo"), false);
1976+
assert_eq!(cmd.opt_arg("foo"), None);
1977+
assert_eq!(cmd.opt_args("foo"), None);
1978+
assert_eq!(cmd.has_opt("bar"), false);
1979+
assert_eq!(cmd.opt_arg("bar"), None);
1980+
assert_eq!(cmd.opt_args("bar"), None);
1981+
1982+
match sub_cmd.parse_for(&mut sub_options) {
1983+
Ok(_) => {
1984+
assert_eq!(sub_cmd.name(), "--foo");
1985+
assert_eq!(sub_cmd.args(), &["sub", "bar", "-@"] as &[&str]);
1986+
assert_eq!(sub_cmd.has_opt("foo"), false);
1987+
assert_eq!(sub_cmd.opt_arg("foo"), None);
1988+
assert_eq!(sub_cmd.opt_args("foo"), None);
1989+
assert_eq!(sub_cmd.has_opt("bar"), false);
1990+
assert_eq!(sub_cmd.opt_arg("bar"), None);
1991+
assert_eq!(sub_cmd.opt_args("bar"), None);
1992+
}
1993+
Err(_) => assert!(false),
1994+
}
1995+
}
1996+
Ok(None) => assert!(false),
1997+
Err(_) => assert!(false),
1998+
}
1999+
}
19122000
}

0 commit comments

Comments
 (0)