Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 348 #387

Merged
merged 2 commits into from
Jan 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 79 additions & 93 deletions src/app/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use SubCommand;
use fmt::Format;
use osstringext::OsStrExt2;
use app::meta::AppMeta;
use args::MatchedArg;

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

if (opt.is_set(ArgSettings::Multiple) || opt.num_vals().is_some())
|| val.is_none() {
if val.is_none() || opt.is_set(ArgSettings::Multiple) {
return Ok(Some(opt.name));
}
Ok(None)
Expand All @@ -1056,18 +1056,24 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
matcher: &mut ArgMatcher<'a>)
-> ClapResult<Option<&'a str>>
where A: AnyArg<'a, 'b> + Display {
matcher.add_val_to(&*arg.name(), val);

// Increment or create the group "args"
if let Some(grps) = self.groups_for_arg(&*arg.name()) {
for grp in grps {
matcher.add_val_to(&*grp, val);
debugln!("fn=add_val_to_arg;");
let mut ret = None;
for v in val.split(b',') {
debugln!("adding val: {:?}", v);
matcher.add_val_to(&*arg.name(), v);

// Increment or create the group "args"
if let Some(grps) = self.groups_for_arg(&*arg.name()) {
for grp in grps {
matcher.add_val_to(&*grp, v);
}
}
}

// The validation must come AFTER inserting into 'matcher' or the usage string
// can't be built
self.validate_value(arg, val, matcher)
// The validation must come AFTER inserting into 'matcher' or the usage string
// can't be built
ret = try!(self.validate_value(arg, v, matcher));
}
Ok(ret)
}

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

fn validate_num_args(&self, matcher: &mut ArgMatcher) -> ClapResult<()> {
debugln!("fn=validate_num_args;");
for (name, ma) in matcher.iter() {
if self.groups.contains_key(&**name) {
continue;
Expand All @@ -1194,97 +1201,76 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
.iter()
.filter(|o| &o.name == name)
.next() {
if let Some(num) = opt.num_vals {
let should_err = if opt.settings.is_set(ArgSettings::Multiple) {
((ma.vals.len() as u8) % num) != 0
} else {
num != (ma.vals.len() as u8)
};
if should_err {
return Err(Error::wrong_number_of_values(
opt,
num,
if opt.settings.is_set(ArgSettings::Multiple) {
(ma.vals.len() % num as usize)
} else {
ma.vals.len()
},
if ma.vals.len() == 1 ||
(opt.settings.is_set(ArgSettings::Multiple) &&
(ma.vals.len() % num as usize) == 1) {
"as"
} else {
"ere"
},
&*self.create_current_usage(matcher)));
}
}
if let Some(num) = opt.max_vals {
if (ma.vals.len() as u8) > num {
return Err(Error::too_many_values(
ma.vals.get(&ma.vals.keys()
.last()
.expect(INTERNAL_ERROR_MSG))
.expect(INTERNAL_ERROR_MSG).to_str().expect(INVALID_UTF8),
opt,
&*self.create_current_usage(matcher)));
}
}
if let Some(num) = opt.min_vals {
if (ma.vals.len() as u8) < num {
return Err(Error::too_few_values(
opt,
num,
ma.vals.len(),
&*self.create_current_usage(matcher)));
}
}
try!(self._validate_num_vals(opt, ma, matcher));
} else if let Some(pos) = self.positionals
.values()
.filter(|p| &p.name == name)
.next() {
if let Some(num) = pos.num_vals {
if num != ma.vals.len() as u8 {
return Err(Error::wrong_number_of_values(
pos,
num,
ma.vals.len(),
if ma.vals.len() == 1 {
"as"
} else {
"ere"
},
&*self.create_current_usage(matcher)));
}
} else if let Some(max) = pos.max_vals {
if (ma.vals.len() as u8) > max {
return Err(
Error::too_many_values(
ma.vals.get(&ma.vals.keys()
.last()
.expect(INTERNAL_ERROR_MSG))
.expect(INTERNAL_ERROR_MSG)
.to_string_lossy()
.into_owned(),
pos,
&*self.create_current_usage(matcher)));
}
}
if let Some(min) = pos.min_vals {
if (ma.vals.len() as u8) < min {
return Err(Error::too_few_values(
pos,
min,
ma.vals.len(),
&*self.create_current_usage(matcher)));
}
}
try!(self._validate_num_vals(pos, ma, matcher));
}
}
}
Ok(())
}

fn _validate_num_vals<A>(&self, a: &A, ma: &MatchedArg, matcher: &ArgMatcher) -> ClapResult<()>
where A: AnyArg<'a, 'b>
{
debugln!("fn=_validate_num_vals;");
if let Some(num) = a.num_vals() {
debugln!("num_vals set: {}", num);
let should_err = if a.is_set(ArgSettings::Multiple) {
((ma.vals.len() as u8) % num) != 0
} else {
num != (ma.vals.len() as u8)
};
if should_err {
debugln!("Sending error WrongNumberOfValues");
return Err(Error::wrong_number_of_values(
a,
num,
if a.is_set(ArgSettings::Multiple) {
(ma.vals.len() % num as usize)
} else {
ma.vals.len()
},
if ma.vals.len() == 1 ||
(a.is_set(ArgSettings::Multiple) &&
(ma.vals.len() % num as usize) == 1) {
"as"
} else {
"ere"
},
&*self.create_current_usage(matcher)));
}
}
if let Some(num) = a.max_vals() {
debugln!("max_vals set: {}", num);
if (ma.vals.len() as u8) > num {
debugln!("Sending error TooManyValues");
return Err(Error::too_many_values(
ma.vals.get(&ma.vals.keys()
.last()
.expect(INTERNAL_ERROR_MSG))
.expect(INTERNAL_ERROR_MSG).to_str().expect(INVALID_UTF8),
a,
&*self.create_current_usage(matcher)));
}
}
if let Some(num) = a.min_vals() {
debugln!("min_vals set: {}", num);
if (ma.vals.len() as u8) < num {
debugln!("Sending error TooFewValues");
return Err(Error::too_few_values(
a,
num,
ma.vals.len(),
&*self.create_current_usage(matcher)));
}
}
Ok(())
}

fn validate_required(&self, matcher: &ArgMatcher) -> ClapResult<()> {
'outer: for name in &self.required {
if matcher.contains(name) {
Expand Down
4 changes: 3 additions & 1 deletion src/args/arg_builder/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ impl<'n, 'e> OptBuilder<'n, 'e> {
ob.settings.set(ArgSettings::Hidden);
}
if let Some(ref vec) = ob.val_names {
ob.num_vals = Some(vec.len() as u8);
if vec.len() > 1 {
ob.num_vals = Some(vec.len() as u8);
}
}
// Check if there is anything in the blacklist (mutually excludes list) and add
// any
Expand Down
73 changes: 73 additions & 0 deletions src/osstringext.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
use std::ffi::OsStr;
#[cfg(not(target_os = "windows"))]
use std::os::unix::ffi::OsStrExt;

#[cfg(target_os = "windows")]
trait OsStrExt3 {
fn from_bytes(b: &[u8]) -> Self;
fn as_bytes(&self) -> &[u8];
}

#[doc(hidden)]
pub trait OsStrExt2 {
fn starts_with(&self, s: &[u8]) -> bool;
fn split_at_byte(&self, b: u8) -> (&OsStr, &OsStr);
Expand All @@ -9,6 +17,18 @@ pub trait OsStrExt2 {
fn len(&self) -> usize;
fn contains_byte(&self, b: u8) -> bool;
fn is_empty(&self) -> bool;
fn split(&self, b: u8) -> OsSplit;
}

#[cfg(target_os = "windows")]
impl OsStrExt3 for OsStr {
fn from_bytes(b: &[u8]) -> Self {
use ::std::mem;
unsafe { mem::transmute(b) }
}
fn as_bytes(&self) -> &[u8] {
self.as_inner().inner
}
}

impl OsStrExt2 for OsStr {
Expand Down Expand Up @@ -52,4 +72,57 @@ impl OsStrExt2 for OsStr {
fn len(&self) -> usize {
self.as_bytes().len()
}

fn split(&self, b: u8) -> OsSplit {
OsSplit { sep: b, val: self.as_bytes(), pos: 0 }
}
}

#[derive(Clone, Debug)]
pub struct OsSplit<'a> {
sep: u8,
val: &'a [u8],
pos: usize,
}

impl<'a> Iterator for OsSplit<'a> {
type Item = &'a OsStr;

fn next(&mut self) -> Option<&'a OsStr> {
debugln!("fn=OsSplit::next;");
debugln!("OsSplit: {:?}", self);
if self.pos == self.val.len() { return None; }
let start = self.pos;
for b in &self.val[start..] {
self.pos += 1;
if *b == self.sep {
return Some(&OsStr::from_bytes(&self.val[start..self.pos - 1]));
}
}
Some(&OsStr::from_bytes(&self.val[start..]))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let mut count = 0;
for b in &self.val[self.pos..] {
if *b == self.sep { count += 1; }
}
if count > 0 {
return (count, Some(count));
}
(0, None)
}
}

impl<'a> DoubleEndedIterator for OsSplit<'a> {
fn next_back(&mut self) -> Option<&'a OsStr> {
if self.pos == 0 { return None; }
let start = self.pos;
for b in self.val[..self.pos].iter().rev() {
self.pos -= 1;
if *b == self.sep {
return Some(&OsStr::from_bytes(&self.val[self.pos + 1..start]));
}
}
Some(&OsStr::from_bytes(&self.val[..start]))
}
}
Loading