use super::*;
use util::*;
use std::{fmt, io};
pub struct Arg<'a, T> {
name: String,
action: Box<Fn(&str) -> Result<T> + 'a>,
short: Option<char>,
long: String,
descr: String,
}
impl<'a, T> fmt::Debug for Arg<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Arg")
.field("name", &self.name)
.field("action", &"…")
.field("short", &self.short)
.field("long", &self.long)
.field("descr", &self.descr)
.finish()
}
}
impl<'a, T> Arg<'a, T> {
pub fn flag<F>(thunk: F) -> Self
where F: Fn() -> T + 'a
{
Self::str_param("", move |_| Ok(thunk()))
}
pub fn str_param<S, F>(name: S, parser: F) -> Self
where S: Into<String>,
F: Fn(&str) -> Result<T> + 'a
{
Arg {
name: name.into(),
action: Box::new(parser),
short: None,
long: String::new(),
descr: String::new(),
}
}
pub fn parsed_param<A, S, F>(name: S, wrapper: F) -> Self
where S: Into<String>,
F: Fn(A) -> T + 'a,
A: FromStr,
A::Err: ToString
{
Arg::str_param(name, move |slice|
slice.parse()
.map(&wrapper)
.map_err(|s| Error::from_string(&s)))
}
pub fn short(mut self, c: char) -> Self {
assert_ne!( c, '-' , "Arg::short: c cannot be '-'" );
self.short = Some(c);
self
}
pub fn long<S: Into<String>>(mut self, s: S) -> Self {
self.long = s.into();
self
}
pub fn description<S: Into<String>>(mut self, s: S) -> Self {
self.descr = s.into();
self
}
pub (crate) fn new_error(&self, long: bool, msg: &str) -> Error {
let opt_name = if long {
format!("--{}", self.long)
} else if let Some(c) = self.short {
format!("-{}", c)
} else {
"-?".to_owned()
};
Error::from_string(msg).with_option(opt_name)
}
pub (crate) fn write_option_usage<W: io::Write>(&self, mut out: W) -> io::Result<()> {
if self.is_positional() { return Ok(()); }
if let Some(c) = self.short {
if self.long.is_empty() {
write!(out, " -{}", c)?;
} else {
write!(out, " -{}, --{}", c, self.long)?;
}
} else {
write!(out, " --{}", self.long)?;
}
if !self.name.is_empty() {
write!(out, " <{}>", self.name)?;
}
if !self.descr.is_empty() {
write!(out, " {}", self.descr)?;
}
writeln!(out)
}
pub (crate) fn is_positional(&self) -> bool {
self.short.is_none() && self.long.is_empty()
}
pub (crate) fn takes_parameter(&self) -> bool {
!self.name.is_empty()
}
pub (crate) fn get_short(&self) -> Option<char> {
self.short
}
pub (crate) fn get_long(&self) -> Option<&str> {
non_empty_string(&self.long)
}
pub (crate) fn positional_name(&self) -> &str {
static ARG: &'static str = "ARG";
if self.name.is_empty() {
ARG
} else {
&self.name
}
}
pub (crate) fn parse_argument<'b>(&self, param: &'b str) -> Result<T> {
(self.action)(param)
}
}