use std::str::FromStr;
use std::fmt::Debug;
use std::borrow::Cow;
use std::rc::Rc;
use help::Help;
use parse::ParseError;
use std::collections::{BinaryHeap, BTreeSet, HashSet, LinkedList, VecDeque};
use std::hash::Hash;
pub type SubCmd<'def> = Box<FnMut(String, &[&str]) -> Result<Option<i32>, ParseError<'def>>>;
pub struct ArgDef<'def, 'tar> {
pub(crate) name: Cow<'def, str>,
pub(crate) kind: ArgDefKind<'def, 'tar>,
pub(crate) help_desc: Option<Cow<'def, str>>,
}
pub(crate) enum ArgDefKind<'def, 'tar> {
Positional {
target: &'tar mut SingleTarget,
},
Subcommand {
handler: SubCmd<'def>,
},
Trail {
target: &'tar mut CollectionTarget,
optional: bool,
},
Flag {
target: &'tar mut bool,
short: Option<Cow<'def, str>>,
},
Count {
target: &'tar mut usize,
short: Option<Cow<'def, str>>,
},
Collect {
target: &'tar mut CollectionTarget,
short: Option<Cow<'def, str>>,
param: Option<Cow<'def, str>>,
},
Setting {
target: &'tar mut OptionTarget,
short: Option<Cow<'def, str>>,
param: Option<Cow<'def, str>>,
},
Interrupt {
callback: Box<FnMut(Rc<Help<'def>>)>,
short: Option<Cow<'def, str>>,
},
}
impl<'def, 'tar> ArgDef<'def, 'tar> {
fn new<N>(name: N, kind: ArgDefKind<'def, 'tar>) -> ArgDef<'def, 'tar>
where N: Into<Cow<'def, str>>
{
ArgDef {
name: name.into(),
kind: kind,
help_desc: None,
}
}
pub fn positional<N>(name: N, target: &'tar mut SingleTarget) -> ArgDef<'def, 'tar>
where N: Into<Cow<'def, str>>
{
ArgDef::new(name, ArgDefKind::Positional { target })
}
pub fn trail<N>(name: N, optional: bool, target: &'tar mut CollectionTarget) -> ArgDef<'def, 'tar>
where N: Into<Cow<'def, str>>
{
ArgDef::new(name, ArgDefKind::Trail { optional, target })
}
pub fn subcommand<N, F>(name: N, handler: F) -> ArgDef<'def, 'tar>
where N: Into<Cow<'def, str>>,
F: 'static + FnMut(String, &[&str]) -> Result<Option<i32>, ParseError<'def>>
{
ArgDef::new(name, ArgDefKind::Subcommand { handler: Box::new(handler) })
}
pub fn interrupt<N, F>(name: N, callback: F) -> ArgDef<'def, 'tar>
where N: Into<Cow<'def, str>>, F: FnMut(Rc<Help<'def>>) + 'static
{
ArgDef::new(name, ArgDefKind::Interrupt {
short: None, callback: Box::new(callback)
})
}
pub fn setting<N>(name: N, target: &'tar mut OptionTarget) -> ArgDef<'def, 'tar>
where N: Into<Cow<'def, str>>
{
ArgDef::new(name, ArgDefKind::Setting { short: None, param: None, target })
}
pub fn flag<N>(name: N, target: &'tar mut bool) -> ArgDef<'def, 'tar>
where N: Into<Cow<'def, str>>
{
ArgDef::new(name, ArgDefKind::Flag { short: None, target })
}
pub fn count<N>(name: N, target: &'tar mut usize) -> ArgDef<'def, 'tar>
where N: Into<Cow<'def, str>>
{
ArgDef::new(name, ArgDefKind::Count { short: None, target })
}
pub fn collect<N>(name: N, target: &'tar mut CollectionTarget) -> ArgDef<'def, 'tar>
where N: Into<Cow<'def, str>>
{
ArgDef::new(name, ArgDefKind::Collect { short: None, param: None, target })
}
pub fn default_help<D: Into<Cow<'static, str>>>(description: D) -> ArgDef<'def, 'tar> {
let description = description.into();
ArgDef::interrupt("help", move |help| {
help.print_help(description.as_ref());
}).help("Print this message and abort.")
}
pub fn default_version() -> ArgDef<'def, 'tar> {
ArgDef::interrupt("version", |_| {
println!("{}", option_env!("CARGO_PKG_VERSION").unwrap_or("0.0.0"));
}).help("Print version string and abort.")
}
pub fn short<N>(mut self, short: N) -> Self where N: Into<Cow<'def, str>> {
use self::ArgDefKind::*;
self.kind = match self.kind {
Positional { .. } | Trail { .. } | Subcommand { .. } => {
println!("WARNING: Positional, trail and subcommand arguments cannot have a short identifier (ArgDef error)");
return self;
},
Flag { target, .. } => Flag { short: Some(short.into()), target },
Count { target, .. } => Count { short: Some(short.into()), target },
Setting { target, param, .. } => Setting { short: Some(short.into()), target, param },
Interrupt { callback, .. } => Interrupt { short: Some(short.into()), callback },
Collect { target, param, .. } => Collect { short: Some(short.into()), target, param },
};
self
}
pub fn param<N>(mut self, parameter_name: N) -> Self where N: Into<Cow<'def, str>> {
use self::ArgDefKind::*;
self.kind = match self.kind {
Setting { target, short, .. } => {
Setting { target, short, param: Some(parameter_name.into()) }
}
Collect { target, short, .. } => {
Collect { target, short, param: Some(parameter_name.into()) }
}
_ => {
println!("WARNING: Only 'option' and 'collect' arguments have a parameter name (ArgDef error)");
return self;
}
};
self
}
pub fn help<N>(mut self, help: N) -> Self where N: Into<Cow<'def, str>> {
self.help_desc = Some(help.into());
self
}
}
pub trait SingleTarget: Debug {
fn parse(&mut self, value: &str) -> Result<(), String>;
}
impl<T> SingleTarget for T where T: Debug + FromStr {
fn parse(&mut self, value: &str) -> Result<(), String> {
let value = match <T as FromStr>::from_str(value) {
Ok(val) => val,
Err(_) => return Err(format!("Could not parse and convert '{}'", value)),
};
*self = value;
Ok(())
}
}
pub trait OptionTarget: Debug {
fn parse(&mut self, value: &str) -> Result<(), String>;
}
impl<T> OptionTarget for Option<T> where T: Debug + FromStr {
fn parse(&mut self, value: &str) -> Result<(), String> {
let value = match <T as FromStr>::from_str(value) {
Ok(val) => val,
Err(_) => return Err(format!("Could not parse and convert '{}'", value)),
};
*self = Some(value);
Ok(())
}
}
pub trait CollectionTarget: Debug {
fn parse_and_add(&mut self, value: &str) -> Result<(), String>;
}
impl<T> CollectionTarget for Vec<T> where T: Debug + FromStr {
fn parse_and_add(&mut self, value: &str) -> Result<(), String> {
let value = match <T as FromStr>::from_str(value) {
Ok(val) => val,
Err(_) => return Err(format!("Could not parse and convert '{}'", value)),
};
self.push(value);
Ok(())
}
}
impl<T> CollectionTarget for BinaryHeap<T> where T: Debug + FromStr + Ord {
fn parse_and_add(&mut self, value: &str) -> Result<(), String> {
let value = match <T as FromStr>::from_str(value) {
Ok(val) => val,
Err(_) => return Err(format!("Could not parse and convert '{}'", value)),
};
self.push(value);
Ok(())
}
}
impl<T> CollectionTarget for BTreeSet<T> where T: Debug + FromStr + Ord {
fn parse_and_add(&mut self, value: &str) -> Result<(), String> {
let value = match <T as FromStr>::from_str(value) {
Ok(val) => val,
Err(_) => return Err(format!("Could not parse and convert '{}'", value)),
};
self.insert(value);
Ok(())
}
}
impl<T> CollectionTarget for HashSet<T> where T: Debug + FromStr + Hash + Eq {
fn parse_and_add(&mut self, value: &str) -> Result<(), String> {
let value = match <T as FromStr>::from_str(value) {
Ok(val) => val,
Err(_) => return Err(format!("Could not parse and convert '{}'", value)),
};
self.insert(value);
Ok(())
}
}
impl<T> CollectionTarget for LinkedList<T> where T: Debug + FromStr {
fn parse_and_add(&mut self, value: &str) -> Result<(), String> {
let value = match <T as FromStr>::from_str(value) {
Ok(val) => val,
Err(_) => return Err(format!("Could not parse and convert '{}'", value)),
};
self.push_back(value);
Ok(())
}
}
impl<T> CollectionTarget for VecDeque<T> where T: Debug + FromStr {
fn parse_and_add(&mut self, value: &str) -> Result<(), String> {
let value = match <T as FromStr>::from_str(value) {
Ok(val) => val,
Err(_) => return Err(format!("Could not parse and convert '{}'", value)),
};
self.push_back(value);
Ok(())
}
}