Implement nested vals

This commit is contained in:
Donough Liu 2021-01-09 18:29:05 +08:00 committed by ldm0
parent 9c52e454e8
commit f1e9b82584
5 changed files with 108 additions and 44 deletions

View file

@ -130,6 +130,11 @@ impl ArgMatcher {
ma.push_val(val);
}
pub(crate) fn add_vals_to(&mut self, arg: &Id, vals: Vec<OsString>, ty: ValueType) {
let ma = self.entry(arg).or_insert(MatchedArg::new(ty));
ma.push_vals(vals);
}
pub(crate) fn add_index_to(&mut self, arg: &Id, idx: usize, ty: ValueType) {
let ma = self.entry(arg).or_insert(MatchedArg::new(ty));
ma.push_index(idx);

View file

@ -3,7 +3,7 @@ use std::{
borrow::Cow,
ffi::{OsStr, OsString},
fmt::{Debug, Display},
iter::{Cloned, Map},
iter::{Cloned, Flatten, Map},
slice::Iter,
str::FromStr,
};
@ -234,14 +234,18 @@ impl ArgMatches {
fn to_str_slice(o: &OsString) -> &str {
o.to_str().expect(INVALID_UTF8)
}
let to_str_slice: fn(&OsString) -> &str = to_str_slice; // coerce to fn pointer
Values {
iter: arg.vals().map(to_str_slice),
iter: arg.vals_flatten().map(to_str_slice),
}
})
}
/// Placeholder documentation.
pub fn grouped_values_of<T: Key>(&self, id: T) -> Option<Iter<Vec<OsString>>> {
self.args.get(&Id::from(id)).map(|arg| arg.vals())
}
/// Gets the lossy values of a specific argument. If the option wasn't present at runtime
/// it returns `None`. A lossy value is one where if it contains invalid UTF-8 code points,
/// those invalid points will be replaced with `\u{FFFD}`
@ -268,7 +272,7 @@ impl ArgMatches {
/// ```
pub fn values_of_lossy<T: Key>(&self, id: T) -> Option<Vec<String>> {
self.args.get(&Id::from(id)).map(|arg| {
arg.vals()
arg.vals_flatten()
.map(|v| v.to_string_lossy().into_owned())
.collect()
})
@ -305,14 +309,13 @@ impl ArgMatches {
/// [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html
/// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html
/// [`String`]: https://doc.rust-lang.org/std/string/struct.String.html
pub fn values_of_os<'a, T: Key>(&'a self, id: T) -> Option<OsValues<'a>> {
pub fn values_of_os<T: Key>(&self, id: T) -> Option<OsValues> {
fn to_str_slice(o: &OsString) -> &OsStr {
&*o
o
}
let to_str_slice: fn(&'a OsString) -> &'a OsStr = to_str_slice; // coerce to fn pointer
self.args.get(&Id::from(id)).map(|arg| OsValues {
iter: arg.vals().map(to_str_slice),
iter: arg.vals_flatten().map(to_str_slice),
})
}
@ -987,7 +990,8 @@ impl ArgMatches {
#[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct Values<'a> {
iter: Map<Iter<'a, OsString>, fn(&'a OsString) -> &'a str>,
#[allow(clippy::type_complexity)]
iter: Map<Flatten<std::slice::Iter<'a, Vec<OsString>>>, for<'r> fn(&'r OsString) -> &'r str>,
}
impl<'a> Iterator for Values<'a> {
@ -1012,13 +1016,13 @@ impl<'a> ExactSizeIterator for Values<'a> {}
/// Creates an empty iterator.
impl<'a> Default for Values<'a> {
fn default() -> Self {
static EMPTY: [OsString; 0] = [];
static EMPTY: [Vec<OsString>; 0] = [];
// This is never called because the iterator is empty:
fn to_str_slice(_: &OsString) -> &str {
unreachable!()
}
Values {
iter: EMPTY[..].iter().map(to_str_slice),
iter: EMPTY[..].iter().flatten().map(to_str_slice),
}
}
}
@ -1047,7 +1051,8 @@ impl<'a> Default for Values<'a> {
#[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct OsValues<'a> {
iter: Map<Iter<'a, OsString>, fn(&'a OsString) -> &'a OsStr>,
#[allow(clippy::type_complexity)]
iter: Map<Flatten<std::slice::Iter<'a, Vec<OsString>>>, fn(&OsString) -> &OsStr>,
}
impl<'a> Iterator for OsValues<'a> {
@ -1072,13 +1077,13 @@ impl<'a> ExactSizeIterator for OsValues<'a> {}
/// Creates an empty iterator.
impl Default for OsValues<'_> {
fn default() -> Self {
static EMPTY: [OsString; 0] = [];
static EMPTY: [Vec<OsString>; 0] = [];
// This is never called because the iterator is empty:
fn to_str_slice(_: &OsString) -> &OsStr {
unreachable!()
}
OsValues {
iter: EMPTY[..].iter().map(to_str_slice),
iter: EMPTY[..].iter().flatten().map(to_str_slice),
}
}
}

View file

@ -1,5 +1,9 @@
// Std
use std::{ffi::{OsStr, OsString}, slice::Iter, iter::Cloned};
use std::{
ffi::{OsStr, OsString},
iter::{Cloned, Flatten},
slice::Iter,
};
// TODO: Maybe make this public?
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -14,7 +18,7 @@ pub(crate) struct MatchedArg {
pub(crate) occurs: u64,
pub(crate) ty: ValueType,
indices: Vec<usize>,
vals: Vec<OsString>,
vals: Vec<Vec<OsString>>,
}
impl MatchedArg {
@ -39,16 +43,24 @@ impl MatchedArg {
self.indices.push(index)
}
pub(crate) fn vals(&self) -> Iter<'_, OsString> {
pub(crate) fn vals(&self) -> Iter<Vec<OsString>> {
self.vals.iter()
}
pub(crate) fn vals_flatten(&self) -> Flatten<Iter<Vec<OsString>>> {
self.vals.iter().flatten()
}
pub(crate) fn get_val(&self, index: usize) -> Option<&OsString> {
self.vals.get(index)
self.vals.get(0)?.get(index)
}
pub(crate) fn push_val(&mut self, val: OsString) {
self.vals.push(val)
self.vals.push(vec![val])
}
pub(crate) fn push_vals(&mut self, vals: Vec<OsString>) {
self.vals.push(vals)
}
pub(crate) fn num_vals(&self) -> usize {
@ -64,8 +76,7 @@ impl MatchedArg {
}
pub(crate) fn contains_val(&self, val: &str) -> bool {
self.vals
.iter()
self.vals_flatten()
.any(|v| OsString::as_os_str(v) == OsStr::new(val))
}
}

View file

@ -1226,46 +1226,88 @@ impl<'help, 'app> Parser<'help, 'app> {
);
if !(self.is_set(AS::TrailingValues) && self.is_set(AS::DontDelimitTrailingValues)) {
if let Some(delim) = arg.val_delim {
let mut iret = ParseResult::ValuesDone;
for v in val.split(delim) {
iret = self.add_single_val_to_arg(arg, &v, matcher, ty);
}
let arg_split = val.split(delim);
let vals = if let Some(t) = arg.terminator {
let mut vals = vec![];
for val in arg_split {
if t == val {
break;
}
vals.push(val);
}
vals
} else {
arg_split.into_iter().collect()
};
let mut parse_result = if vals.is_empty() {
ParseResult::ValuesDone
} else {
self.add_multiple_val_to_arg(arg, vals, matcher, ty)
};
// If there was a delimiter used or we must use the delimiter to
// separate the values, we're not looking for more values.
if val.contains_char(delim) || arg.is_set(ArgSettings::RequireDelimiter) {
iret = ParseResult::ValuesDone;
parse_result = ParseResult::ValuesDone;
}
return iret;
return parse_result;
}
}
if let Some(t) = arg.terminator {
if t == val {
return ParseResult::ValuesDone;
}
}
self.add_single_val_to_arg(arg, val, matcher, ty)
}
fn add_single_val_to_arg(
fn add_multiple_val_to_arg(
&self,
arg: &Arg<'help>,
v: &ArgStr,
vals: Vec<ArgStr>,
matcher: &mut ArgMatcher,
ty: ValueType,
) -> ParseResult {
debug!("Parser::add_single_val_to_arg: adding val...{:?}", v);
debug!("Parser::add_multiple_val_to_arg: adding vals...{:?}", vals);
let num_vals = vals.len();
let begin_index = self.cur_idx.get() + 1;
self.cur_idx.set(self.cur_idx.get() + num_vals);
for i in begin_index..begin_index + num_vals {
matcher.add_index_to(&arg.id, i, ty);
}
let vals: Vec<_> = vals.into_iter().map(|x| x.to_os_string()).collect();
// Increment or create the group "args"
for val in vals.iter() {
for group in self.app.groups_for_arg(&arg.id) {
matcher.add_val_to(&group, val.clone(), ty);
}
}
matcher.add_vals_to(&arg.id, vals, ty);
if matcher.needs_more_vals(arg) {
ParseResult::Opt(arg.id.clone())
} else {
ParseResult::ValuesDone
}
}
fn add_single_val_to_arg(
&self,
arg: &Arg<'help>,
val: &ArgStr,
matcher: &mut ArgMatcher,
ty: ValueType,
) -> ParseResult {
debug!("Parser::add_single_val_to_arg: adding val...{:?}", val);
// update the current index because each value is a distinct index to clap
self.cur_idx.set(self.cur_idx.get() + 1);
// @TODO @docs @p4 docs should probably note that terminator doesn't get an index
if let Some(t) = arg.terminator {
if t == v {
return ParseResult::ValuesDone;
}
}
matcher.add_val_to(&arg.id, v.to_os_string(), ty);
matcher.add_val_to(&arg.id, val.to_os_string(), ty);
matcher.add_index_to(&arg.id, self.cur_idx.get(), ty);
// Increment or create the group "args"
for group in self.app.groups_for_arg(&arg.id) {
matcher.add_val_to(&group, v.to_os_string(), ty);
matcher.add_val_to(&group, val.to_os_string(), ty);
}
if matcher.needs_more_vals(arg) {
@ -1379,7 +1421,7 @@ impl<'help, 'app> Parser<'help, 'app> {
for (id, val, default) in arg.default_vals_ifs.values() {
let add = if let Some(a) = matcher.get(&id) {
if let Some(v) = val {
a.vals().any(|value| v == value)
a.vals_flatten().any(|value| v == value)
} else {
true
}

View file

@ -82,7 +82,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
matcher: &ArgMatcher,
) -> ClapResult<()> {
debug!("Validator::validate_arg_values: arg={:?}", arg.name);
for val in ma.vals() {
for val in ma.vals_flatten() {
if self.p.is_set(AS::StrictUtf8) && val.to_str().is_none() {
debug!(
"Validator::validate_arg_values: invalid UTF-8 found in val {:?}",
@ -399,7 +399,8 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
for (name, ma) in matcher.iter() {
debug!(
"Validator::validate_matched_args:iter:{:?}: vals={:#?}",
name, ma.vals()
name,
ma.vals_flatten()
);
if let Some(arg) = self.p.app.find(name) {
self.validate_arg_num_vals(arg, ma)?;
@ -467,7 +468,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
if (ma.num_vals() as u64) > num {
debug!("Validator::validate_arg_num_vals: Sending error TooManyValues");
return Err(Error::too_many_values(
ma.vals()
ma.vals_flatten()
.last()
.expect(INTERNAL_ERROR_MSG)
.to_str()
@ -517,7 +518,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
for (val, name) in &a.requires {
if let Some(val) = val {
let missing_req = |v| v == val && !matcher.contains(&name);
if ma.vals().any(missing_req) {
if ma.vals_flatten().any(missing_req) {
return self.missing_required_error(matcher, vec![a.id.clone()]);
}
} else if !matcher.contains(&name) {