Skip to content

Commit 1b52757

Browse files
committed
Add support for detecting args after double-dash
The crate provides support for detecting and ignoring double-dash ("--") argument passed from command line, but it does not provide any information about its presence or position to user. For applications that may want to pass arguments after double-dash to child process is such indication critical. Add optional field `args_end` into Matches structure storing position of first argument after double-dash (if there is any) and method to provide user access to the data. When checking for double-dash positin, it is important to make sure that there actually are some arguments after it. Otherwise the position would point to non-existent index in `free` list and would require additional unnecessary checks on user side.
1 parent a1bc6a8 commit 1b52757

File tree

2 files changed

+77
-3
lines changed

2 files changed

+77
-3
lines changed

src/lib.rs

+50-1
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,8 @@ impl Options {
347347
.map(|_| Vec::new())
348348
.collect::<Vec<Vec<(usize, Optval)>>>();
349349
let mut free: Vec<String> = Vec::new();
350+
let mut args_end = None;
351+
350352
let args = args
351353
.into_iter()
352354
.map(|i| {
@@ -369,6 +371,7 @@ impl Options {
369371
}
370372
}
371373
} else if cur == "--" {
374+
args_end = Some(free.len());
372375
free.extend(args);
373376
break;
374377
} else {
@@ -471,7 +474,12 @@ impl Options {
471474
return Err(OptionDuplicated(opt.name.to_string()));
472475
}
473476
}
474-
Ok(Matches { opts, vals, free })
477+
478+
// Note that if "--" is last argument on command line, then index stored
479+
// in option does not exist in `free` and must be replaced with `None`
480+
args_end = args_end.filter(|pos| pos != &free.len());
481+
482+
Ok(Matches { opts, vals, free, args_end })
475483
}
476484

477485
/// Derive a short one-line usage summary from a set of long options.
@@ -692,8 +700,12 @@ pub struct Matches {
692700
opts: Vec<Opt>,
693701
/// Values of the Options that matched and their positions
694702
vals: Vec<Vec<(usize, Optval)>>,
703+
695704
/// Free string fragments
696705
pub free: Vec<String>,
706+
707+
/// Index of first free fragment after "--" separator
708+
args_end: Option<usize>,
697709
}
698710

699711
/// The type returned when the command line does not conform to the
@@ -1018,6 +1030,43 @@ impl Matches {
10181030
None => Ok(def),
10191031
}
10201032
}
1033+
1034+
/// Returns index of first free argument after "--".
1035+
///
1036+
/// If double-dash separator is present and there are some args after it in
1037+
/// the argument list then the method returns index into `free` vector
1038+
/// indicating first argument after it.
1039+
/// behind it.
1040+
///
1041+
/// # Examples
1042+
///
1043+
/// ```
1044+
/// # use getopts::Options;
1045+
/// let mut opts = Options::new();
1046+
///
1047+
/// let matches = opts.parse(&vec!["arg1", "--", "arg2"]).unwrap();
1048+
/// let end_pos = matches.free_trailing_start().unwrap();
1049+
/// assert_eq!(end_pos, 1);
1050+
/// assert_eq!(matches.free[end_pos], "arg2".to_owned());
1051+
/// ```
1052+
///
1053+
/// If the double-dash is missing from argument list or if there are no
1054+
/// arguments after it:
1055+
///
1056+
/// ```
1057+
/// # use getopts::Options;
1058+
/// let mut opts = Options::new();
1059+
///
1060+
/// let matches = opts.parse(&vec!["arg1", "--"]).unwrap();
1061+
/// assert_eq!(matches.free_trailing_start(), None);
1062+
///
1063+
/// let matches = opts.parse(&vec!["arg1", "arg2"]).unwrap();
1064+
/// assert_eq!(matches.free_trailing_start(), None);
1065+
/// ```
1066+
///
1067+
pub fn free_trailing_start(&self) -> Option<usize> {
1068+
self.args_end
1069+
}
10211070
}
10221071

10231072
fn is_arg(arg: &str) -> bool {

src/tests/mod.rs

+27-2
Original file line numberDiff line numberDiff line change
@@ -207,12 +207,24 @@ fn test_optflag_missing() {
207207
}
208208

209209
#[test]
210-
fn test_opt_end() {
210+
fn test_free_trailing_missing() {
211+
let args = vec![] as Vec<String>;
212+
match Options::new().parse(&args) {
213+
Ok(ref m) => {
214+
assert_eq!(m.free_trailing_start(), None);
215+
}
216+
_ => panic!(),
217+
}
218+
}
219+
220+
#[test]
221+
fn test_free_trailing() {
211222
let args = vec!["--".to_owned(), "-t".to_owned()];
212223
match Options::new().optflag("t", "test", "testing").parse(&args) {
213224
Ok(ref m) => {
214225
assert!(!m.opt_present("test"));
215226
assert!(!m.opt_present("t"));
227+
assert_eq!(m.free_trailing_start(), Some(0));
216228
assert_eq!(m.free.len(), 1);
217229
assert_eq!(m.free[0], "-t");
218230
}
@@ -221,18 +233,31 @@ fn test_opt_end() {
221233
}
222234

223235
#[test]
224-
fn test_opt_only_end() {
236+
fn test_free_trailing_only() {
225237
let args = vec!["--".to_owned()];
226238
match Options::new().optflag("t", "test", "testing").parse(&args) {
227239
Ok(ref m) => {
228240
assert!(!m.opt_present("test"));
229241
assert!(!m.opt_present("t"));
242+
assert_eq!(m.free_trailing_start(), None);
230243
assert_eq!(m.free.len(), 0);
231244
}
232245
_ => panic!(),
233246
}
234247
}
235248

249+
#[test]
250+
fn test_free_trailing_args() {
251+
let args = vec!["pre".to_owned(), "--".to_owned(), "post".to_owned() ];
252+
match Options::new().parse(&args) {
253+
Ok(ref m) => {
254+
assert_eq!(m.free_trailing_start(), Some(1));
255+
assert_eq!(m.free.len(), 2);
256+
}
257+
_ => panic!(),
258+
}
259+
}
260+
236261
#[test]
237262
fn test_optflag_long_arg() {
238263
let args = vec!["--test=20".to_string()];

0 commit comments

Comments
 (0)