Skip to content

Commit e69da6a

Browse files
committed
feat: adds support for comma separated values
This commit adds support for values separated by commas such as --option=val1,val2,val3. It also includes support for uses without the equals and shorts (both with and without) --option=val1,val2 --option val1,val2 -oval1,val2 -o=val1,val2 -o val1,val2 Closes #348
1 parent 4b8b5b6 commit e69da6a

File tree

3 files changed

+155
-94
lines changed

3 files changed

+155
-94
lines changed

src/app/parser.rs

+79-93
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use SubCommand;
2222
use fmt::Format;
2323
use osstringext::OsStrExt2;
2424
use app::meta::AppMeta;
25+
use args::MatchedArg;
2526

2627
pub struct Parser<'a, 'b> where 'a: 'b {
2728
required: Vec<&'b str>,
@@ -1043,8 +1044,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
10431044
// Increment or create the group "args"
10441045
self.groups_for_arg(opt.name).and_then(|vec| Some(matcher.inc_occurrences_of(&*vec)));
10451046

1046-
if (opt.is_set(ArgSettings::Multiple) || opt.num_vals().is_some())
1047-
|| val.is_none() {
1047+
if val.is_none() || opt.is_set(ArgSettings::Multiple) {
10481048
return Ok(Some(opt.name));
10491049
}
10501050
Ok(None)
@@ -1056,18 +1056,24 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
10561056
matcher: &mut ArgMatcher<'a>)
10571057
-> ClapResult<Option<&'a str>>
10581058
where A: AnyArg<'a, 'b> + Display {
1059-
matcher.add_val_to(&*arg.name(), val);
1060-
1061-
// Increment or create the group "args"
1062-
if let Some(grps) = self.groups_for_arg(&*arg.name()) {
1063-
for grp in grps {
1064-
matcher.add_val_to(&*grp, val);
1059+
debugln!("fn=add_val_to_arg;");
1060+
let mut ret = None;
1061+
for v in val.split(b',') {
1062+
debugln!("adding val: {:?}", v);
1063+
matcher.add_val_to(&*arg.name(), v);
1064+
1065+
// Increment or create the group "args"
1066+
if let Some(grps) = self.groups_for_arg(&*arg.name()) {
1067+
for grp in grps {
1068+
matcher.add_val_to(&*grp, v);
1069+
}
10651070
}
1066-
}
10671071

1068-
// The validation must come AFTER inserting into 'matcher' or the usage string
1069-
// can't be built
1070-
self.validate_value(arg, val, matcher)
1072+
// The validation must come AFTER inserting into 'matcher' or the usage string
1073+
// can't be built
1074+
ret = try!(self.validate_value(arg, v, matcher));
1075+
}
1076+
Ok(ret)
10711077
}
10721078

10731079
fn validate_value<A>(&self, arg: &A, val: &OsStr, matcher: &ArgMatcher<'a>) -> ClapResult<Option<&'a str>>
@@ -1186,6 +1192,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
11861192
}
11871193

11881194
fn validate_num_args(&self, matcher: &mut ArgMatcher) -> ClapResult<()> {
1195+
debugln!("fn=validate_num_args;");
11891196
for (name, ma) in matcher.iter() {
11901197
if self.groups.contains_key(&**name) {
11911198
continue;
@@ -1194,97 +1201,76 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
11941201
.iter()
11951202
.filter(|o| &o.name == name)
11961203
.next() {
1197-
if let Some(num) = opt.num_vals {
1198-
let should_err = if opt.settings.is_set(ArgSettings::Multiple) {
1199-
((ma.vals.len() as u8) % num) != 0
1200-
} else {
1201-
num != (ma.vals.len() as u8)
1202-
};
1203-
if should_err {
1204-
return Err(Error::wrong_number_of_values(
1205-
opt,
1206-
num,
1207-
if opt.settings.is_set(ArgSettings::Multiple) {
1208-
(ma.vals.len() % num as usize)
1209-
} else {
1210-
ma.vals.len()
1211-
},
1212-
if ma.vals.len() == 1 ||
1213-
(opt.settings.is_set(ArgSettings::Multiple) &&
1214-
(ma.vals.len() % num as usize) == 1) {
1215-
"as"
1216-
} else {
1217-
"ere"
1218-
},
1219-
&*self.create_current_usage(matcher)));
1220-
}
1221-
}
1222-
if let Some(num) = opt.max_vals {
1223-
if (ma.vals.len() as u8) > num {
1224-
return Err(Error::too_many_values(
1225-
ma.vals.get(&ma.vals.keys()
1226-
.last()
1227-
.expect(INTERNAL_ERROR_MSG))
1228-
.expect(INTERNAL_ERROR_MSG).to_str().expect(INVALID_UTF8),
1229-
opt,
1230-
&*self.create_current_usage(matcher)));
1231-
}
1232-
}
1233-
if let Some(num) = opt.min_vals {
1234-
if (ma.vals.len() as u8) < num {
1235-
return Err(Error::too_few_values(
1236-
opt,
1237-
num,
1238-
ma.vals.len(),
1239-
&*self.create_current_usage(matcher)));
1240-
}
1241-
}
1204+
try!(self._validate_num_vals(opt, ma, matcher));
12421205
} else if let Some(pos) = self.positionals
12431206
.values()
12441207
.filter(|p| &p.name == name)
12451208
.next() {
1246-
if let Some(num) = pos.num_vals {
1247-
if num != ma.vals.len() as u8 {
1248-
return Err(Error::wrong_number_of_values(
1249-
pos,
1250-
num,
1251-
ma.vals.len(),
1252-
if ma.vals.len() == 1 {
1253-
"as"
1254-
} else {
1255-
"ere"
1256-
},
1257-
&*self.create_current_usage(matcher)));
1258-
}
1259-
} else if let Some(max) = pos.max_vals {
1260-
if (ma.vals.len() as u8) > max {
1261-
return Err(
1262-
Error::too_many_values(
1263-
ma.vals.get(&ma.vals.keys()
1264-
.last()
1265-
.expect(INTERNAL_ERROR_MSG))
1266-
.expect(INTERNAL_ERROR_MSG)
1267-
.to_string_lossy()
1268-
.into_owned(),
1269-
pos,
1270-
&*self.create_current_usage(matcher)));
1271-
}
1272-
}
1273-
if let Some(min) = pos.min_vals {
1274-
if (ma.vals.len() as u8) < min {
1275-
return Err(Error::too_few_values(
1276-
pos,
1277-
min,
1278-
ma.vals.len(),
1279-
&*self.create_current_usage(matcher)));
1280-
}
1281-
}
1209+
try!(self._validate_num_vals(pos, ma, matcher));
12821210
}
12831211
}
12841212
}
12851213
Ok(())
12861214
}
12871215

1216+
fn _validate_num_vals<A>(&self, a: &A, ma: &MatchedArg, matcher: &ArgMatcher) -> ClapResult<()>
1217+
where A: AnyArg<'a, 'b>
1218+
{
1219+
debugln!("fn=_validate_num_vals;");
1220+
if let Some(num) = a.num_vals() {
1221+
debugln!("num_vals set: {}", num);
1222+
let should_err = if a.is_set(ArgSettings::Multiple) {
1223+
((ma.vals.len() as u8) % num) != 0
1224+
} else {
1225+
num != (ma.vals.len() as u8)
1226+
};
1227+
if should_err {
1228+
debugln!("Sending error WrongNumberOfValues");
1229+
return Err(Error::wrong_number_of_values(
1230+
a,
1231+
num,
1232+
if a.is_set(ArgSettings::Multiple) {
1233+
(ma.vals.len() % num as usize)
1234+
} else {
1235+
ma.vals.len()
1236+
},
1237+
if ma.vals.len() == 1 ||
1238+
(a.is_set(ArgSettings::Multiple) &&
1239+
(ma.vals.len() % num as usize) == 1) {
1240+
"as"
1241+
} else {
1242+
"ere"
1243+
},
1244+
&*self.create_current_usage(matcher)));
1245+
}
1246+
}
1247+
if let Some(num) = a.max_vals() {
1248+
debugln!("max_vals set: {}", num);
1249+
if (ma.vals.len() as u8) > num {
1250+
debugln!("Sending error TooManyValues");
1251+
return Err(Error::too_many_values(
1252+
ma.vals.get(&ma.vals.keys()
1253+
.last()
1254+
.expect(INTERNAL_ERROR_MSG))
1255+
.expect(INTERNAL_ERROR_MSG).to_str().expect(INVALID_UTF8),
1256+
a,
1257+
&*self.create_current_usage(matcher)));
1258+
}
1259+
}
1260+
if let Some(num) = a.min_vals() {
1261+
debugln!("min_vals set: {}", num);
1262+
if (ma.vals.len() as u8) < num {
1263+
debugln!("Sending error TooFewValues");
1264+
return Err(Error::too_few_values(
1265+
a,
1266+
num,
1267+
ma.vals.len(),
1268+
&*self.create_current_usage(matcher)));
1269+
}
1270+
}
1271+
Ok(())
1272+
}
1273+
12881274
fn validate_required(&self, matcher: &ArgMatcher) -> ClapResult<()> {
12891275
'outer: for name in &self.required {
12901276
if matcher.contains(name) {

src/args/arg_builder/option.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,9 @@ impl<'n, 'e> OptBuilder<'n, 'e> {
9999
ob.settings.set(ArgSettings::Hidden);
100100
}
101101
if let Some(ref vec) = ob.val_names {
102-
ob.num_vals = Some(vec.len() as u8);
102+
if vec.len() > 1 {
103+
ob.num_vals = Some(vec.len() as u8);
104+
}
103105
}
104106
// Check if there is anything in the blacklist (mutually excludes list) and add
105107
// any

src/osstringext.rs

+73
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
use std::ffi::OsStr;
2+
#[cfg(not(target_os = "windows"))]
23
use std::os::unix::ffi::OsStrExt;
34

5+
#[cfg(target_os = "windows")]
6+
trait OsStrExt3 {
7+
fn from_bytes(b: &[u8]) -> Self;
8+
fn as_bytes(&self) -> &[u8];
9+
}
10+
11+
#[doc(hidden)]
412
pub trait OsStrExt2 {
513
fn starts_with(&self, s: &[u8]) -> bool;
614
fn split_at_byte(&self, b: u8) -> (&OsStr, &OsStr);
@@ -9,6 +17,18 @@ pub trait OsStrExt2 {
917
fn len(&self) -> usize;
1018
fn contains_byte(&self, b: u8) -> bool;
1119
fn is_empty(&self) -> bool;
20+
fn split(&self, b: u8) -> OsSplit;
21+
}
22+
23+
#[cfg(target_os = "windows")]
24+
impl OsStrExt3 for OsStr {
25+
fn from_bytes(b: &[u8]) -> Self {
26+
use ::std::mem;
27+
unsafe { mem::transmute(b) }
28+
}
29+
fn as_bytes(&self) -> &[u8] {
30+
self.as_inner().inner
31+
}
1232
}
1333

1434
impl OsStrExt2 for OsStr {
@@ -52,4 +72,57 @@ impl OsStrExt2 for OsStr {
5272
fn len(&self) -> usize {
5373
self.as_bytes().len()
5474
}
75+
76+
fn split(&self, b: u8) -> OsSplit {
77+
OsSplit { sep: b, val: self.as_bytes(), pos: 0 }
78+
}
79+
}
80+
81+
#[derive(Clone, Debug)]
82+
pub struct OsSplit<'a> {
83+
sep: u8,
84+
val: &'a [u8],
85+
pos: usize,
86+
}
87+
88+
impl<'a> Iterator for OsSplit<'a> {
89+
type Item = &'a OsStr;
90+
91+
fn next(&mut self) -> Option<&'a OsStr> {
92+
debugln!("fn=OsSplit::next;");
93+
debugln!("OsSplit: {:?}", self);
94+
if self.pos == self.val.len() { return None; }
95+
let start = self.pos;
96+
for b in &self.val[start..] {
97+
self.pos += 1;
98+
if *b == self.sep {
99+
return Some(&OsStr::from_bytes(&self.val[start..self.pos - 1]));
100+
}
101+
}
102+
Some(&OsStr::from_bytes(&self.val[start..]))
103+
}
104+
fn size_hint(&self) -> (usize, Option<usize>) {
105+
let mut count = 0;
106+
for b in &self.val[self.pos..] {
107+
if *b == self.sep { count += 1; }
108+
}
109+
if count > 0 {
110+
return (count, Some(count));
111+
}
112+
(0, None)
113+
}
114+
}
115+
116+
impl<'a> DoubleEndedIterator for OsSplit<'a> {
117+
fn next_back(&mut self) -> Option<&'a OsStr> {
118+
if self.pos == 0 { return None; }
119+
let start = self.pos;
120+
for b in self.val[..self.pos].iter().rev() {
121+
self.pos -= 1;
122+
if *b == self.sep {
123+
return Some(&OsStr::from_bytes(&self.val[self.pos + 1..start]));
124+
}
125+
}
126+
Some(&OsStr::from_bytes(&self.val[..start]))
127+
}
55128
}

0 commit comments

Comments
 (0)