Skip to content

Commit c11eb65

Browse files
authored
Merge pull request #100 from zdenek-crha/parse_args_end_position
Add support for detecting args after double-dash
2 parents 6e5c619 + 1b52757 commit c11eb65

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
@@ -433,6 +433,8 @@ impl Options {
433433
.map(|_| Vec::new())
434434
.collect::<Vec<Vec<(usize, Optval)>>>();
435435
let mut free: Vec<String> = Vec::new();
436+
let mut args_end = None;
437+
436438
let args = args
437439
.into_iter()
438440
.map(|i| {
@@ -455,6 +457,7 @@ impl Options {
455457
}
456458
}
457459
} else if cur == "--" {
460+
args_end = Some(free.len());
458461
free.extend(args);
459462
break;
460463
} else {
@@ -557,7 +560,12 @@ impl Options {
557560
return Err(OptionDuplicated(opt.name.to_string()));
558561
}
559562
}
560-
Ok(Matches { opts, vals, free })
563+
564+
// Note that if "--" is last argument on command line, then index stored
565+
// in option does not exist in `free` and must be replaced with `None`
566+
args_end = args_end.filter(|pos| pos != &free.len());
567+
568+
Ok(Matches { opts, vals, free, args_end })
561569
}
562570

563571
/// Derive a short one-line usage summary from a set of long options.
@@ -778,8 +786,12 @@ pub struct Matches {
778786
opts: Vec<Opt>,
779787
/// Values of the Options that matched and their positions
780788
vals: Vec<Vec<(usize, Optval)>>,
789+
781790
/// Free string fragments
782791
pub free: Vec<String>,
792+
793+
/// Index of first free fragment after "--" separator
794+
args_end: Option<usize>,
783795
}
784796

785797
/// The type returned when the command line does not conform to the
@@ -1104,6 +1116,43 @@ impl Matches {
11041116
None => Ok(def),
11051117
}
11061118
}
1119+
1120+
/// Returns index of first free argument after "--".
1121+
///
1122+
/// If double-dash separator is present and there are some args after it in
1123+
/// the argument list then the method returns index into `free` vector
1124+
/// indicating first argument after it.
1125+
/// behind it.
1126+
///
1127+
/// # Examples
1128+
///
1129+
/// ```
1130+
/// # use getopts::Options;
1131+
/// let mut opts = Options::new();
1132+
///
1133+
/// let matches = opts.parse(&vec!["arg1", "--", "arg2"]).unwrap();
1134+
/// let end_pos = matches.free_trailing_start().unwrap();
1135+
/// assert_eq!(end_pos, 1);
1136+
/// assert_eq!(matches.free[end_pos], "arg2".to_owned());
1137+
/// ```
1138+
///
1139+
/// If the double-dash is missing from argument list or if there are no
1140+
/// arguments after it:
1141+
///
1142+
/// ```
1143+
/// # use getopts::Options;
1144+
/// let mut opts = Options::new();
1145+
///
1146+
/// let matches = opts.parse(&vec!["arg1", "--"]).unwrap();
1147+
/// assert_eq!(matches.free_trailing_start(), None);
1148+
///
1149+
/// let matches = opts.parse(&vec!["arg1", "arg2"]).unwrap();
1150+
/// assert_eq!(matches.free_trailing_start(), None);
1151+
/// ```
1152+
///
1153+
pub fn free_trailing_start(&self) -> Option<usize> {
1154+
self.args_end
1155+
}
11071156
}
11081157

11091158
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)