#[macro_use] extern crate error_chain;
#[macro_use] extern crate maplit;
extern crate serde;
use serde::de;
use std::collections::HashMap;
use std::collections::hash_map;
use std::env;
use std::iter::Peekable;
mod error {
use serde::de;
use std::fmt::Display;
error_chain! {
errors {
DuplicateFlag(flag: String) {
description("encountered duplicate flag"),
display("encoutered duplicate flag value for: {}", flag),
}
UnrecognizedFlag(flag: String) {
description("encountered unrecognized flag"),
display("encountered unrecognized flag: {}", flag),
}
TooManyPositionalArguments(max_args: usize) {
description("recieved too many arguments"),
display("recieved more than {} arguments", max_args),
}
}
}
impl de::Error for Error {
fn custom<T: Display>(msg: T) -> Self {
msg.to_string().into()
}
}
}
pub use error::Error as VexillumError;
#[derive(Debug, PartialEq, Clone)]
pub struct ArgumentsSchema {
positional_names: Vec<String>,
remainder_name: Option<String>,
flags: FlagsConfig
}
#[derive(Debug, PartialEq, Clone)]
struct FlagsConfig {
name_to_flag: HashMap<String, Flag>,
long_flag_to_name: HashMap<String, String>,
short_flag_to_name: HashMap<char, String>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct Flag {
field_name: String,
flag_type: FlagType,
long_flag: String,
short_flag: Option<char>,
}
fn kebab_to_snake(identifier: &String) -> String {
identifier.chars().map(|c| if c == '-' { '_' } else { c }).collect()
}
impl Flag {
pub fn new(flag: String, flag_type: FlagType) -> Self {
let field_name = kebab_to_snake(&flag);
Flag {
field_name: field_name,
flag_type: flag_type,
long_flag: flag,
short_flag: None,
}
}
pub fn store_to(mut self, field_name: String) -> Self {
self.field_name = field_name;
self
}
pub fn short(mut self, flag: char) -> Self {
self.short_flag = Some(flag);
self
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum FlagType {
Boolean,
Parameter,
MultiParameter,
}
impl ArgumentsSchema {
pub fn new() -> Self {
ArgumentsSchema{
positional_names: Vec::new(),
remainder_name: None,
flags: FlagsConfig::new(),
}
}
pub fn positional(mut self, name: String) -> Self {
self.positional_names.push(name);
self
}
pub fn flag(mut self, flag_config: Flag) -> Self {
self.flags.flag(flag_config);
self
}
pub fn remainder(mut self, name: String) -> Self {
self.remainder_name = Some(name);
self
}
}
impl FlagsConfig {
fn new() -> Self {
return FlagsConfig {
name_to_flag: HashMap::new(),
long_flag_to_name: HashMap::new(),
short_flag_to_name: HashMap::new(),
}
}
fn flag(&mut self, flag_config: Flag) {
self.long_flag_to_name.insert(flag_config.long_flag.clone(), flag_config.field_name.clone());
if let Some(short_flag) = flag_config.short_flag.clone() {
self.short_flag_to_name.insert(short_flag, flag_config.field_name.clone());
}
self.name_to_flag.insert(flag_config.field_name.clone(), flag_config);
}
}
#[derive(Debug, PartialEq, Clone)]
enum IntermediateValue {
Boolean(bool),
Parameter(String),
MultiParameter(Vec<String>),
}
fn parse_args_to_intermediate(args: Vec<String>, schema: ArgumentsSchema) -> error::Result<HashMap<String, IntermediateValue>> {
let mut mapping = HashMap::new();
let mut i = 0;
let mut positionals = 0;
let mut end_of_flags = false;
while i < args.len() {
let arg = &args[i];
if !end_of_flags && arg.starts_with("-") {
if arg == "--" {
end_of_flags = true;
} else if arg.starts_with("--") {
let long_flag_identifier = &arg[2..];
if let Some(name) = schema.flags.long_flag_to_name.get(long_flag_identifier) {
let flag_config = schema.flags.name_to_flag.get(name).unwrap();
if mapping.contains_key(name) && flag_config.flag_type != FlagType::MultiParameter {
return Err(error::ErrorKind::DuplicateFlag(flag_config.long_flag.clone()).into());
} else {
let value = match flag_config.flag_type {
FlagType::Boolean => IntermediateValue::Boolean(true),
FlagType::Parameter => {
i = i + 1;
IntermediateValue::Parameter(args[i].clone())
}
FlagType::MultiParameter => {
i += 1;
match mapping.get(name).cloned().unwrap_or(IntermediateValue::MultiParameter(vec![])) {
IntermediateValue::MultiParameter(old_list) => {
let mut list = old_list.clone();
list.push(args[i].clone());
IntermediateValue::MultiParameter(list)
},
_ => return Err("can't get here".into()),
}
}
};
mapping.insert(name.clone(), value);
}
} else {
return Err(error::ErrorKind::UnrecognizedFlag(arg.clone()).into());
}
} else if arg.starts_with("-") && arg.len() >= 2 {
for (_, short_flag_identifier) in arg.chars().enumerate() {
if let Some(name) = schema.flags.short_flag_to_name.get(&short_flag_identifier) {
let flag_config = schema.flags.name_to_flag.get(name).unwrap();
if mapping.contains_key(name) && flag_config.flag_type != FlagType::MultiParameter {
return Err(error::ErrorKind::DuplicateFlag(format!("-{}", short_flag_identifier)).into());
} else {
let value = match flag_config.flag_type {
FlagType::Boolean => IntermediateValue::Boolean(true),
FlagType::Parameter => {
i = i + 1;
IntermediateValue::Parameter(args[i].clone())
},
FlagType::MultiParameter => {
i += 1;
match mapping.get(name).cloned().unwrap_or(IntermediateValue::MultiParameter(vec![])) {
IntermediateValue::MultiParameter(old_list) => {
let mut list = old_list.clone();
list.push(args[i].clone());
IntermediateValue::MultiParameter(list)
},
_ => return Err("can't get here".into()),
}
},
};
mapping.insert(name.clone(), value);
}
}
}
}
} else {
if positionals >= schema.positional_names.len() {
if let Some(remainder_name) = &schema.remainder_name {
if mapping.contains_key(remainder_name) {
match mapping.get_mut(remainder_name).unwrap() {
IntermediateValue::MultiParameter(value) => {
value.push(arg.clone());
}
_ => return Err("encountered bad value for remainder".into()),
}
} else {
mapping.insert(
remainder_name.clone(),
IntermediateValue::MultiParameter(vec![arg.clone()]),
);
};
} else {
return Err(error::ErrorKind::TooManyPositionalArguments(schema.positional_names.len()).into())
}
} else {
let name = &schema.positional_names[positionals];
mapping.insert(
name.clone(),
IntermediateValue::Parameter(arg.clone()),
);
}
positionals += 1;
}
i += 1;
}
for (name, flag) in schema.flags.name_to_flag.iter() {
if !mapping.contains_key(name) {
match flag.flag_type {
FlagType::Boolean => {
mapping.insert(
name.clone(),
IntermediateValue::Boolean(false),
);
},
FlagType::Parameter => {
},
FlagType::MultiParameter => {
mapping.insert(
name.clone(),
IntermediateValue::MultiParameter(vec![]),
);
},
}
}
}
if let Some(remainder_name) = schema.remainder_name {
if !mapping.contains_key(&remainder_name) {
mapping.insert(
remainder_name.clone(),
IntermediateValue::MultiParameter(vec![]),
);
}
}
Ok(mapping)
}
struct Deserializer {
intermediate: Box<Peekable<hash_map::IntoIter<String, IntermediateValue>>>
}
impl Deserializer {
fn from_args(args: Vec<String>, schema: ArgumentsSchema) -> error::Result<Self> {
let intermediate = Box::new(parse_args_to_intermediate(args, schema)?.into_iter().peekable());
Ok(Deserializer{intermediate})
}
}
impl<'de, 'a: 'de> de::Deserializer<'de> for Deserializer {
type Error = error::Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_byte_buf<V>(
self,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_unit_struct<V>(
self,
name: &'static str,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_newtype_struct<V>(
self,
name: &'static str,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_tuple<V>(
self,
len: usize,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_tuple_struct<V>(
self,
name: &'static str,
len: usize,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_struct<V>(
mut self,
name: &'static str,
fields: &'static [&'static str],
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{
visitor.visit_map(StructDeserializer{de: &mut self})
}
fn deserialize_enum<V>(
self,
name: &'static str,
variants: &'static [&'static str],
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_identifier<V>(
self,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_ignored_any<V>(
self,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
}
struct StructDeserializer<'a> {
de: &'a mut Deserializer,
}
impl<'de, 'a> de::MapAccess<'de> for StructDeserializer<'a> {
type Error = error::Error;
fn next_key_seed<K>(&mut self, seed: K) -> error::Result<Option<K::Value>> where K: de::DeserializeSeed<'de> {
match self.de.intermediate.peek() {
None => Ok(None),
Some(item) => {
let (key, _) = item;
seed.deserialize(MapKey{key: key.clone()}).map(Some)
},
}
}
fn next_value_seed<K: de::DeserializeSeed<'de>>(&mut self, seed: K) -> error::Result<K::Value>{
match self.de.intermediate.next() {
None => Err("no map value".into()),
Some(item) => {
let (_, value) = item;
seed.deserialize(ValueDeserializer{value: value})
}
}
}
}
struct MapKey {
key: String,
}
impl<'de, 'a: 'de> de::Deserializer<'de> for MapKey {
type Error = error::Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_byte_buf<V>(
self,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_unit_struct<V>(
self,
name: &'static str,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_newtype_struct<V>(
self,
name: &'static str,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_tuple<V>(
self,
len: usize,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_tuple_struct<V>(
self,
name: &'static str,
len: usize,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_enum<V>(
self,
name: &'static str,
variants: &'static [&'static str],
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_identifier<V>(
self,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{
visitor.visit_string(self.key.clone())
}
fn deserialize_ignored_any<V>(
self,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
}
struct ValueDeserializer {
value: IntermediateValue,
}
impl<'de, 'a: 'de> de::Deserializer<'de> for ValueDeserializer {
type Error = error::Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{
match self.value {
IntermediateValue::Boolean(value) => visitor.visit_bool(value),
_ => Err("looking for boolean but got something else".into()),
}
}
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{
match self.value {
IntermediateValue::Parameter(value) => visitor.visit_string(value),
_ => Err("looking for string but got something else".into()),
}
}
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_byte_buf<V>(
self,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{
visitor.visit_some(self)
}
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_unit_struct<V>(
self,
name: &'static str,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_newtype_struct<V>(
self,
name: &'static str,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{
match self.value {
IntermediateValue::MultiParameter(value) => visitor.visit_seq(SeqAccess{value: value, i: 0}),
_ => Err("non-sequence value".into()),
}
}
fn deserialize_tuple<V>(
self,
len: usize,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_tuple_struct<V>(
self,
name: &'static str,
len: usize,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_enum<V>(
self,
name: &'static str,
variants: &'static [&'static str],
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
fn deserialize_identifier<V>(
self,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{
unimplemented!();
}
fn deserialize_ignored_any<V>(
self,
visitor: V
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>{ unimplemented!(); }
}
struct SeqAccess {
value: Vec<String>,
i: usize,
}
impl<'de> de::SeqAccess<'de> for SeqAccess {
type Error = error::Error;
fn next_element_seed<T>(&mut self, seed: T) -> error::Result<Option<T::Value>> where T: de::DeserializeSeed<'de> {
let i = self.i;
if i >= self.value.len() {
return Ok(None)
}
self.i += 1;
seed.deserialize(ValueDeserializer{value: IntermediateValue::Parameter(self.value[i].clone())}).map(Some)
}
}
fn _parse_args<'a, T>(args: Vec<String>, schema: ArgumentsSchema) -> error::Result<T> where T: de::Deserialize<'a> {
let mut deserializer = Deserializer::from_args(args, schema)?;
let t = T::deserialize(deserializer)?;
Ok(t)
}
pub fn parse_args<'a, T>(schema: ArgumentsSchema) -> error::Result<T> where T: de::Deserialize<'a> {
let args = env::args().skip(1).collect();
_parse_args(args, schema)
}
#[cfg(test)]
mod test {
use super::*;
use serde::Deserialize;
#[test]
fn test_add_positional() {
assert_eq!(
ArgumentsSchema::new()
.positional("foo".to_owned())
.positional("bar".to_owned()),
ArgumentsSchema{
positional_names: vec!["foo".to_owned(), "bar".to_owned()],
remainder_name: None,
flags: FlagsConfig::new(),
},
);
}
#[test]
fn test_add_flag() {
let schema = ArgumentsSchema::new()
.flag(
Flag::new(
"foo".to_owned(),
FlagType::Boolean,
)
.short('f'),
)
.flag(
Flag::new(
"bar".to_owned(),
FlagType::Parameter,
)
);
assert_eq!(
schema,
ArgumentsSchema{
positional_names: Vec::new(),
remainder_name: None,
flags: FlagsConfig{
name_to_flag: hashmap!{
"foo".to_owned() => Flag{
field_name: "foo".to_owned(),
long_flag: "foo".to_owned(),
short_flag: Some('f'),
flag_type: FlagType::Boolean,
},
"bar".to_owned() => Flag{
field_name: "bar".to_owned(),
long_flag: "bar".to_owned(),
short_flag: None,
flag_type: FlagType::Parameter,
},
},
long_flag_to_name: hashmap!{
"foo".to_owned() => "foo".to_owned(),
"bar".to_owned() => "bar".to_owned(),
},
short_flag_to_name: hashmap!{
'f' => "foo".to_owned(),
},
},
},
);
}
#[test]
fn test_parse_args_to_intermediate() {
let schema = ArgumentsSchema::new()
.flag(
Flag::new(
"foo".to_owned(),
FlagType::Boolean,
)
)
.flag(
Flag::new(
"bar".to_owned(),
FlagType::Parameter,
)
);
assert_eq!(
parse_args_to_intermediate(vec!["--foo".to_owned(), "--bar".to_owned(), "baz".to_owned()], schema).unwrap(),
hashmap!{
"foo".to_owned() => IntermediateValue::Boolean(true),
"bar".to_owned() => IntermediateValue::Parameter("baz".to_owned()),
},
);
}
#[derive(Debug, PartialEq, Deserialize)]
struct Args {
foo: bool,
bar: Option<String>,
quux: String,
foobar: Vec<String>,
}
#[test]
fn test_deserialize() {
let schema = ArgumentsSchema::new()
.flag(
Flag::new(
"foo".to_owned(),
FlagType::Boolean,
)
.short('f'),
)
.flag(
Flag::new(
"bar".to_owned(),
FlagType::Parameter,
)
)
.positional("quux".to_owned())
.remainder("foobar".to_owned());
let parsed_args: Args = _parse_args(vec!["--foo".to_owned(), "--bar".to_owned(), "baz".to_owned(), "spam".to_owned()], schema.clone()).unwrap();
assert_eq!(
parsed_args,
Args{
foo: true,
bar: Some("baz".to_owned()),
quux: "spam".to_owned(),
foobar: vec![],
},
);
let parsed_args2: Args = _parse_args(vec!["spam".to_owned()], schema.clone()).unwrap();
assert_eq!(
parsed_args2,
Args{
foo: false,
bar: None,
quux: "spam".to_owned(),
foobar: vec![],
},
);
let parsed_args3: Args = _parse_args(vec!["-f".to_owned(), "spam".to_owned()], schema.clone()).unwrap();
assert_eq!(
parsed_args3,
Args{
foo: true,
bar: None,
quux: "spam".to_owned(),
foobar: vec![],
},
);
let parsed_args4: Args = _parse_args(vec!["spam".to_owned(), "eggs".to_owned(), "spamneggs".to_owned()], schema.clone()).unwrap();
assert_eq!(
parsed_args4,
Args{
foo: false,
bar: None,
quux: "spam".to_owned(),
foobar: vec!["eggs".to_owned(), "spamneggs".to_owned()],
},
);
}
#[test]
fn test_end_of_flags() {
let schema = ArgumentsSchema::new()
.flag(
Flag::new(
"foo".to_owned(),
FlagType::Boolean,
)
.short('f')
)
.flag(
Flag::new(
"bar".to_owned(),
FlagType::Parameter,
)
)
.positional("quux".to_owned())
.remainder("foobar".to_owned());
let parsed_args: Args = _parse_args(
vec!["-f".to_owned(), "--".to_owned(), "--bar".to_owned(), "--foo".to_owned()],
schema,
).unwrap();
assert_eq!(
parsed_args,
Args{
foo: true,
bar: None,
quux: "--bar".to_owned(),
foobar: vec!["--foo".to_owned()],
},
);
}
#[test]
fn test_multi_short_flag() {
#[derive(Deserialize, PartialEq, Debug)]
struct Args {
foo: bool,
bar: bool,
quux: bool,
}
let schema = ArgumentsSchema::new()
.flag(
Flag::new(
"foo".to_owned(),
FlagType::Boolean,
)
.short('f')
)
.flag(
Flag::new(
"bar".to_owned(),
FlagType::Boolean,
)
.short('b')
)
.flag(
Flag::new(
"quux".to_owned(),
FlagType::Boolean,
)
.short('q')
);
let parsed_args: Args = _parse_args(
vec!["-bfq".to_owned()],
schema,
).unwrap();
assert_eq!(
parsed_args,
Args{
foo: true,
bar: true,
quux: true,
},
);
}
#[test]
fn test_multi_parameter() {
#[derive(Deserialize, PartialEq, Debug)]
struct Args {
foo: Vec<String>,
}
let schema = ArgumentsSchema::new()
.flag(
Flag::new(
"foo".to_owned(),
FlagType::MultiParameter,
)
.short('f'),
);
let parsed_args: Args = _parse_args(
vec!["-f".to_owned(), "bar".to_owned(), "-f".to_owned(), "baz".to_owned(), "-f".to_owned(), "quux".to_owned()],
schema,
).unwrap();
assert_eq!(
parsed_args,
Args{
foo: vec!["bar".to_owned(), "baz".to_owned(), "quux".to_owned()],
},
);
}
}