use error::*;
use flags::spec::{Spec, Specs, Type};
use std::collections::HashMap;
use std::iter::{FromIterator, Peekable};
use std::str::FromStr;
fn get_default_values<'a>(specs: &Specs) -> HashMap<String, Value> {
specs
.iter()
.filter(|s| s.has_default_value())
.map(|s| -> (String, Value) {
match s.flag_type {
Type::Required { ref default_value } => (
s.name.clone(),
Value::Single(default_value.as_ref().unwrap().clone()),
),
Type::Boolean => (s.name.clone(), Value::Boolean(false)),
Type::Positional {
ref default_value, ..
} => (
s.name.clone(),
Value::Repeated(default_value.as_ref().unwrap().clone()),
),
_ => panic!("Default value lookup for {:?} not implemented", s.flag_type),
}
})
.collect()
}
fn parse_bool(value: &str) -> Result<bool> {
match value.trim().to_lowercase().as_ref() {
"true" => Ok(true),
"false" => Ok(false),
_ => {
return Err(Error::InvalidArgument(format_err!(
"Invalid boolean value '{}'",
value
)))
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum Value {
Single(String),
Boolean(bool),
Repeated(Vec<String>),
}
impl Value {
pub fn new_named_flag_value(spec: &Spec, value: Option<String>) -> Result<Value> {
Ok(match spec.is_boolean() {
false => Value::Single(match value {
None => {
return Err(Error::InvalidArgument(format_err!(
"Missing value for flag '{}'",
spec.get_name()
)))
}
Some(value) => value,
}),
true => Value::Boolean(match value {
None => true,
Some(value) => parse_bool(value.as_str())?,
}),
})
}
}
struct ParsedNamedFlag {
name: String,
value: Value,
}
struct NamedFlagSpec<'a> {
pub value: Option<String>,
pub spec: &'a Spec,
}
impl<'a> NamedFlagSpec<'a> {
pub fn new<'b>(specs: &'a Specs, flag: &'b str) -> Result<NamedFlagSpec<'a>> {
let trimmed = if flag.starts_with("--") {
&flag[2..]
} else {
&flag[1..]
};
let equals_idx = trimmed.rfind('=');
let name = equals_idx.map_or(trimmed, |ei| &trimmed[0..ei]);
let value = equals_idx.map_or(None, |ei| Some((&trimmed[ei + 1..]).to_owned()));
let spec: &'a Spec = match specs.find_named_spec(name) {
Some(s) => s,
None => {
return Err(Error::InvalidArgument(format_err!(
"Unrecognized flag '{}'",
name
)))
}
};
Ok(NamedFlagSpec {
value: value,
spec: spec,
})
}
}
struct PositionalFlagSpec {
pub name: String,
pub is_variadic: bool,
}
fn parse_next_named_flag<'a, 'b, I: Iterator<Item = &'b String>>(
specs: &'a Specs,
args: &mut Peekable<I>,
) -> Result<Option<ParsedNamedFlag>> {
let flag: &'b String = match args.peek() {
Some(p) => if p.starts_with('-') {
p
} else {
return Ok(None);
},
None => return Ok(None),
};
args.next();
let mut spec = NamedFlagSpec::new(specs, flag)?;
if !spec.spec.is_boolean() {
let next_arg_is_value = args.peek().map_or(false, |v| !v.starts_with('-'));
if next_arg_is_value && spec.value.is_none() {
spec.value = Some(args.next().unwrap().clone());
}
}
Ok(Some(ParsedNamedFlag {
name: spec.spec.get_name().to_owned(),
value: Value::new_named_flag_value(&spec.spec, spec.value)?,
}))
}
struct ValueIterator<'a, 'b, I: Iterator<Item = &'b String>> {
specs: &'a Specs,
args: Peekable<I>,
finished_named_flags: bool,
positional_specs: Vec<PositionalFlagSpec>,
}
impl<'a, 'b, I: Iterator<Item = &'b String>> ValueIterator<'a, 'b, I> {
pub fn new(specs: &'a Specs, args: Peekable<I>) -> ValueIterator<'a, 'b, I> {
ValueIterator {
specs: specs,
args: args,
finished_named_flags: false,
positional_specs: specs
.iter()
.filter_map(|s| match s.is_positional() {
false => None,
true => Some(PositionalFlagSpec {
name: s.get_name().to_owned(),
is_variadic: s.is_variadic(),
}),
})
.rev()
.collect(),
}
}
}
impl<'a, 'b, I: Iterator<Item = &'b String>> Iterator for ValueIterator<'a, 'b, I> {
type Item = Result<(String, Value)>;
fn next(&mut self) -> Option<Self::Item> {
if !self.finished_named_flags {
match parse_next_named_flag(self.specs, &mut self.args) {
Ok(parsed_flag) => match parsed_flag {
Some(parsed_flag) => return Some(Ok((parsed_flag.name, parsed_flag.value))),
None => self.finished_named_flags = true,
},
Err(e) => return Some(Err(e)),
}
}
match self.positional_specs.pop() {
None => None,
Some(spec) => match spec.is_variadic {
false => match self.args.next() {
None => None,
Some(value) => Some(Ok((spec.name, Value::Repeated(vec![value.clone()])))),
},
true => {
let mut values = vec![];
while let Some(value) = self.args.next() {
values.push(value.clone());
}
Some(Ok((spec.name, Value::Repeated(values))))
}
},
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct Values {
values: HashMap<String, Value>,
}
impl Values {
pub fn new<'a, 'b, I: Iterator<Item = &'b String>>(
specs: &'a Specs,
args: Peekable<I>,
) -> Result<Values> {
let default_values = get_default_values(specs);
let values: Result<HashMap<String, Value>> = ValueIterator::new(specs, args).collect();
let mut values: HashMap<String, Value> = values?;
for (name, value) in default_values.into_iter() {
values.entry(name).or_insert(value);
}
for s in specs.iter() {
if s.is_required() && !values.contains_key(&s.name) {
return Err(Error::InvalidArgument(format_err!(
"Unexpected missing value for flag '{}'",
s.name
)));
}
}
Ok(Values { values: values })
}
pub fn get(&self, name: &str) -> Option<&Value> {
self.values.get(name)
}
pub fn get_single(&self, name: &str) -> Option<&str> {
match self.values.get(name) {
None => None,
Some(v) => match v {
&Value::Single(ref s) => Some(s.as_str()),
_ => panic!("Flag '{}' is not a named non-boolean flag", name),
},
}
}
pub fn get_required(&self, name: &str) -> &str {
match self.values.get(name) {
None => panic!("Missing required flag value for '{}'", name),
Some(v) => match v {
&Value::Single(ref s) => s.as_str(),
_ => panic!("Flag '{}' is not a named non-boolean flag", name),
},
}
}
pub fn get_required_as<T: From<String>>(&self, name: &str) -> T {
T::from(self.get_required(name).to_owned())
}
pub fn get_required_parsed<E, T: FromStr<Err = E>>(
&self,
name: &str,
) -> ::std::result::Result<T, E> {
self.get_required(name).parse()
}
pub fn get_boolean(&self, name: &str) -> bool {
match self.values.get(name) {
None => panic!("Missing boolean flag value for '{}'", name),
Some(v) => match v {
&Value::Boolean(b) => b,
_ => panic!("Flag '{}' is not a named boolean flag", name),
},
}
}
pub fn get_positional(&self, name: &str) -> &[String] {
match self.values.get(name) {
None => panic!("Missing positional flag value for '{}'", name),
Some(v) => match v {
&Value::Repeated(ref vs) => vs.as_slice(),
_ => panic!("Flag '{}' is not a positional flag", name),
},
}
}
pub fn get_positional_single(&self, name: &str) -> &str {
let vs = self.get_positional(name);
if vs.len() > 1 {
panic!(
"Positional flag '{}' has more than one associated value",
name
);
}
match vs.first() {
None => panic!("Positional flag '{}' has an empty list of values", name),
Some(v) => v.as_str(),
}
}
}
impl From<HashMap<String, Value>> for Values {
fn from(values: HashMap<String, Value>) -> Self {
Values { values: values }
}
}
impl FromIterator<(String, Value)> for Values {
fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> Self {
let values: HashMap<String, Value> = iter.into_iter().collect();
values.into()
}
}