Repository: kbknapp/clap-rs
Pull request #1198 (2018-03-01 12:03:02)

Port 1161 fix to v3
Copy the fix from https://github.com/kbknapp/clap-rs/pull/1162 into
the v3 parser, and add a test to protect against regressions in the
expected behavior of example 22.

Note: I haven't been able to lint this - `cargo build --features lints` seems to fail on current nightly. I assume CI will tell me if I messed up linting, but I wanted to mention it.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/kbknapp/clap-rs/1198)
<!-- Reviewable:end -->



Patch:
diff --git a/src/app/parser.rs b/src/app/parser.rs
index ce483b22..3d1dfe5c 100644
--- a/src/app/parser.rs
+++ b/src/app/parser.rs
@@ -8,7 +8,7 @@ use std::iter::Peekable;
 use std::mem;
 
 // Third party
-use vec_map::VecMap;
+use map::{self, VecMap};
 
 // Internal
 use INTERNAL_ERROR_MSG;
@@ -109,6 +109,7 @@ where
         }
     }
 
+
     #[cfg_attr(feature = "lints", allow(block_in_if_condition_stmt))]
     fn _verify_positionals(&mut self) -> bool {
         debugln!("Parser::_verify_positionals;");
@@ -118,7 +119,18 @@ where
         // Firt we verify that the index highest supplied index, is equal to the number of
         // positional arguments to verify there are no gaps (i.e. supplying an index of 1 and 3
         // but no 2)
-        let highest_idx = self.positionals.keys().last().unwrap_or(0);
+        #[cfg(feature = "vec_map")]
+        fn _highest_idx(map: &VecMap<&str>) -> usize {
+            map.keys().last().unwrap_or(0)
+        }
+
+        #[cfg(not(feature = "vec_map"))]
+        fn _highest_idx(map: &VecMap<&str>) -> usize {
+            *map.keys().last().unwrap_or(&0)
+        }
+
+        let highest_idx = _highest_idx(&self.positionals);
+
         let num_p = self.positionals.len();
 
         assert!(
@@ -369,7 +381,8 @@ where
             self.unset(AS::ValidNegNumFound);
             // Is this a new argument, or values from a previous option?
             let starts_new_arg = self.is_new_arg(&arg_os, needs_val_of);
-            if arg_os.starts_with(b"--") && arg_os.len_() == 2 && starts_new_arg {
+            if !self.is_set(AS::TrailingValues) &&
+                arg_os.starts_with(b"--") && arg_os.len_() == 2 && starts_new_arg {
                 debugln!("Parser::get_matches_with: setting TrailingVals=true");
                 self.set(AS::TrailingValues);
                 continue;
diff --git a/src/map.rs b/src/map.rs
index 063a8602..345f0412 100644
--- a/src/map.rs
+++ b/src/map.rs
@@ -32,6 +32,8 @@ mod vec_map {
 
         pub fn values(&self) -> Values<V> { self.inner.values() }
 
+        pub fn keys(&self) -> btree_map::Keys<usize, V> { self.inner.keys() }
+
         pub fn iter(&self) -> Iter<V> {
             Iter {
                 inner: self.inner.iter(),


Test patch:
diff --git a/tests/subcommands.rs b/tests/subcommands.rs
index 388916c7..ff2f19ba 100644
--- a/tests/subcommands.rs
+++ b/tests/subcommands.rs
@@ -212,3 +212,23 @@ fn issue_1031_args_with_same_name_no_more_vals() {
     assert_eq!(m.value_of("ui-path"), Some("value"));
     assert_eq!(m.subcommand_name(), Some("signer"));
 }
+
+#[test]
+fn issue_1161_multiple_hyphen_hyphen() {
+    // from example 22
+    let res = App::new("myprog")
+        .arg(Arg::with_name("eff").short("f"))
+        .arg(Arg::with_name("pea").short("p").takes_value(true))
+        .arg(Arg::with_name("slop").multiple(true).last(true))
+        .get_matches_from_safe(vec!["-f", "-p=bob", "--", "sloppy", "slop", "-a", "--", "subprogram", "position", "args"]);
+
+    assert!(res.is_ok(), "{:?}", res.unwrap_err().kind);
+    let m = res.unwrap();
+
+    let expected = Some(vec!["sloppy", "slop", "-a", "--", "subprogram", "position", "args"]);
+    let actual = m
+            .values_of("slop")
+            .map(|vals| vals.collect::<Vec<_>>());
+
+    assert_eq!(expected, actual);
+}
\ No newline at end of file