#![deny(missing_docs)]
extern crate clap as _clap;
extern crate meval;
#[allow(unused_imports)]
#[macro_use]
extern crate clapme_derive;
#[doc(hidden)]
pub use clapme_derive::*;
use std::str::FromStr;
use std::ffi::OsString;
pub mod guide;
pub mod clap {
pub use _clap::*;
}
#[derive(Clone)]
pub struct ArgInfo<'a> {
pub name: &'a str,
pub required: bool,
pub required_flags: &'a [&'a str],
pub conflicted_flags: Vec<String>,
pub required_unless_one: Vec<String>,
pub multiple: bool,
pub help: &'a str,
}
impl<'a> ArgInfo<'a> {
pub fn new(name: &'a str) -> Self {
ArgInfo {
name: name,
required: true,
required_flags: &[],
multiple: false,
help: "",
conflicted_flags: Vec::new(),
required_unless_one: Vec::new(),
}
}
}
pub trait ClapMe : Sized {
fn with_clap<T>(_info: ArgInfo,
app: clap::App,
f: impl FnOnce(clap::App) -> T) -> T {
f(app)
}
fn from_clap(_name: &str, _app: &clap::ArgMatches) -> Option<Self> {
None
}
fn requires_flags(name: &str) -> Vec<String> {
vec![name.to_string()]
}
fn help_message(cmdname: &str) -> String {
let info = ArgInfo::new("");
Self::with_clap(info, clap::App::new(cmdname),
|a| {
let mut help_data = Vec::new();
a.write_help(&mut help_data).unwrap();
String::from_utf8_lossy(&help_data).into_owned()
})
}
fn from_args() -> Self {
Self::with_clap(ArgInfo::new(""),
clap::App::new("foo"),
|a| {
let matches = a.get_matches();
Self::from_clap("", &matches).unwrap()
})
}
fn from_iter<I,T>(args: I) -> Result<Self, clap::Error>
where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
{
Self::with_clap(ArgInfo::new(""),
clap::App::new("foo"),
|a| {
let matches = a.get_matches_from_safe(args)?;
Ok(Self::from_clap("", &matches).unwrap())
})
}
}
impl ClapMe for bool {
fn with_clap<T>(info: ArgInfo, app: clap::App,
f: impl FnOnce(clap::App) -> T) -> T {
let conflicts: Vec<_> = info.conflicted_flags.iter().map(AsRef::as_ref).collect();
f(app.arg(clap::Arg::with_name(info.name).long(info.name)
.requires_all(info.required_flags)
.conflicts_with_all(&conflicts)
.help(&info.help)))
}
fn from_clap(name: &str, matches: &clap::ArgMatches) -> Option<Self> {
Some(matches.is_present(name))
}
fn requires_flags(_name: &str) -> Vec<String> {
vec![]
}
}
macro_rules! impl_fromstr {
($t:ty, $tyname:expr) => {
impl ClapMe for $t {
fn with_clap<T>(info: ArgInfo, app: clap::App,
f: impl FnOnce(clap::App) -> T) -> T {
let conflicts: Vec<_> = info.conflicted_flags.iter().map(AsRef::as_ref).collect();
let ruo: Vec<_> = info.required_unless_one.iter().map(AsRef::as_ref).collect();
if info.name == "" {
f(app.arg(clap::Arg::with_name(info.name)
.takes_value(true)
.value_name($tyname)
.requires_all(info.required_flags)
.required(info.required)
.help(&info.help)
.validator(|s| Self::from_str(&s).map(|_| ())
.map_err(|e| e.to_string()))))
} else if ruo.len() > 0 {
f(app.arg(clap::Arg::with_name(info.name)
.long(info.name)
.takes_value(true)
.value_name($tyname)
.requires_all(info.required_flags)
.required(info.required)
.conflicts_with_all(&conflicts)
.required_unless_one(&ruo)
.help(&info.help)
.validator(|s| Self::from_str(&s).map(|_| ())
.map_err(|e| e.to_string()))))
} else {
f(app.arg(clap::Arg::with_name(info.name)
.long(info.name)
.takes_value(true)
.value_name($tyname)
.requires_all(info.required_flags)
.required(info.required)
.conflicts_with_all(&conflicts)
.help(&info.help)
.validator(|s| Self::from_str(&s).map(|_| ())
.map_err(|e| e.to_string()))))
}
}
fn from_clap(name: &str, matches: &clap::ArgMatches) -> Option<Self> {
matches.value_of(name).map(|s| Self::from_str(s).unwrap())
}
}
impl ClapMe for Vec<$t> {
fn with_clap<TT>(info: ArgInfo, app: clap::App,
f: impl FnOnce(clap::App) -> TT) -> TT {
let conflicts: Vec<_> = info.conflicted_flags.iter().map(AsRef::as_ref).collect();
if info.name == "" {
f(app.arg(clap::Arg::with_name(info.name)
.takes_value(true)
.value_name($tyname)
.required(false)
.requires_all(info.required_flags)
.multiple(true)
.help(&info.help)
.validator(|s| <$t>::from_str(&s).map(|_| ())
.map_err(|_| "oops".to_owned()))))
} else {
f(app.arg(clap::Arg::with_name(info.name)
.long(info.name)
.takes_value(true)
.value_name($tyname)
.required(false)
.requires_all(info.required_flags)
.conflicts_with_all(&conflicts)
.multiple(true)
.help(&info.help)
.validator(|s| <$t>::from_str(&s).map(|_| ())
.map_err(|_| "oops".to_owned()))))
}
}
fn from_clap(name: &str, matches: &clap::ArgMatches) -> Option<Self> {
Some(matches.values_of(name).unwrap_or(clap::Values::default())
.map(|s| <$t>::from_str(s).unwrap()).collect())
}
fn requires_flags(_name: &str) -> Vec<String> {
vec![]
}
}
}
}
fn str_to_f64(s: &str) -> Result<f64, String> {
meval::eval_str(s).map_err(|e| e.to_string())
}
macro_rules! impl_rustyard {
($t:ty, $tyname:expr) => {
impl ClapMe for $t {
fn with_clap<T>(info: ArgInfo, app: clap::App,
f: impl FnOnce(clap::App) -> T) -> T {
let conflicts: Vec<_> = info.conflicted_flags.iter().map(AsRef::as_ref).collect();
let ruo: Vec<_> = info.required_unless_one.iter().map(AsRef::as_ref).collect();
if info.name == "" {
f(app.arg(clap::Arg::with_name(info.name)
.takes_value(true)
.value_name($tyname)
.requires_all(info.required_flags)
.required(info.required)
.help(&info.help)
.validator(|s| str_to_f64(&s).map(|_| ()))))
} else if ruo.len() > 0 {
f(app.arg(clap::Arg::with_name(info.name)
.long(info.name)
.takes_value(true)
.value_name($tyname)
.requires_all(info.required_flags)
.required(info.required)
.conflicts_with_all(&conflicts)
.required_unless_one(&ruo)
.help(&info.help)
.validator(|s| str_to_f64(&s).map(|_| ()))))
} else {
f(app.arg(clap::Arg::with_name(info.name)
.long(info.name)
.takes_value(true)
.value_name($tyname)
.requires_all(info.required_flags)
.required(info.required)
.conflicts_with_all(&conflicts)
.help(&info.help)
.validator(|s| str_to_f64(&s).map(|_| ()))))
}
}
fn from_clap(name: &str, matches: &clap::ArgMatches) -> Option<Self> {
matches.value_of(name).map(|s| str_to_f64(s).unwrap() as Self)
}
}
impl ClapMe for Vec<$t> {
fn with_clap<TT>(info: ArgInfo, app: clap::App,
f: impl FnOnce(clap::App) -> TT) -> TT {
let conflicts: Vec<_> = info.conflicted_flags.iter().map(AsRef::as_ref).collect();
if info.name == "" {
f(app.arg(clap::Arg::with_name(info.name)
.takes_value(true)
.value_name($tyname)
.required(false)
.requires_all(info.required_flags)
.multiple(true)
.help(&info.help)
.validator(|s| str_to_f64(&s).map(|_| ()))))
} else {
f(app.arg(clap::Arg::with_name(info.name)
.long(info.name)
.takes_value(true)
.value_name($tyname)
.required(false)
.requires_all(info.required_flags)
.conflicts_with_all(&conflicts)
.multiple(true)
.help(&info.help)
.validator(|s| str_to_f64(&s).map(|_| ()))))
}
}
fn from_clap(name: &str, matches: &clap::ArgMatches) -> Option<Self> {
Some(matches.values_of(name).unwrap_or(clap::Values::default())
.map(|s| str_to_f64(s).unwrap() as $t).collect())
}
fn requires_flags(_name: &str) -> Vec<String> {
vec![]
}
}
}
}
impl_rustyard!(f32, "FLOAT");
impl_rustyard!(f64, "FLOAT");
impl_fromstr!(std::net::IpAddr, "ADDR");
impl_fromstr!(std::net::Ipv4Addr, "ADDR");
impl_fromstr!(std::net::Ipv6Addr, "ADDR");
impl_fromstr!(std::net::SocketAddr, "ADDR:PORT");
impl_fromstr!(std::net::SocketAddrV4, "ADDR:PORT");
impl_fromstr!(std::net::SocketAddrV6, "ADDR:PORT");
macro_rules! impl_fromstr_or_via_f64 {
($t:ty, $tyname:expr) => {
impl ClapMe for $t {
fn with_clap<T>(info: ArgInfo, app: clap::App,
f: impl FnOnce(clap::App) -> T) -> T {
let conflicts: Vec<_> = info.conflicted_flags.iter().map(AsRef::as_ref).collect();
let ruo: Vec<_> = info.required_unless_one.iter().map(AsRef::as_ref).collect();
let validator = |s: String| {
if let Err(e) = Self::from_str(&s) {
if let Ok(x) = str_to_f64(&s) {
if (x as $t) as f64 == x {
return Ok(());
}
}
return Err(e.to_string());
}
Ok(())
};
if info.name == "" {
f(app.arg(clap::Arg::with_name(info.name)
.takes_value(true)
.value_name($tyname)
.requires_all(info.required_flags)
.required(info.required)
.help(&info.help)
.validator(validator)))
} else if ruo.len() > 0 {
f(app.arg(clap::Arg::with_name(info.name)
.long(info.name)
.takes_value(true)
.value_name($tyname)
.requires_all(info.required_flags)
.required(info.required)
.conflicts_with_all(&conflicts)
.required_unless_one(&ruo)
.help(&info.help)
.validator(validator)))
} else {
f(app.arg(clap::Arg::with_name(info.name)
.long(info.name)
.takes_value(true)
.value_name($tyname)
.requires_all(info.required_flags)
.required(info.required)
.conflicts_with_all(&conflicts)
.help(&info.help)
.validator(validator)))
}
}
fn from_clap(name: &str, matches: &clap::ArgMatches) -> Option<Self> {
matches.value_of(name).map(|s| {
if let Ok(v) = Self::from_str(s) {
return v;
}
str_to_f64(s).unwrap() as Self
})
}
}
impl ClapMe for Vec<$t> {
fn with_clap<TT>(info: ArgInfo, app: clap::App,
f: impl FnOnce(clap::App) -> TT) -> TT {
let conflicts: Vec<_> = info.conflicted_flags.iter().map(AsRef::as_ref).collect();
if info.name == "" {
f(app.arg(clap::Arg::with_name(info.name)
.takes_value(true)
.value_name($tyname)
.required(false)
.requires_all(info.required_flags)
.multiple(true)
.help(&info.help)
.validator(|s| <$t>::from_str(&s).map(|_| ())
.map_err(|_| "oops".to_owned()))))
} else {
f(app.arg(clap::Arg::with_name(info.name)
.long(info.name)
.takes_value(true)
.value_name($tyname)
.required(false)
.requires_all(info.required_flags)
.conflicts_with_all(&conflicts)
.multiple(true)
.help(&info.help)
.validator(|s| <$t>::from_str(&s).map(|_| ())
.map_err(|_| "oops".to_owned()))))
}
}
fn from_clap(name: &str, matches: &clap::ArgMatches) -> Option<Self> {
Some(matches.values_of(name).unwrap_or(clap::Values::default())
.map(|s| <$t>::from_str(s).unwrap()).collect())
}
fn requires_flags(_name: &str) -> Vec<String> {
vec![]
}
}
}
}
impl_fromstr_or_via_f64!(i16, "INT");
impl_fromstr_or_via_f64!(isize, "INT");
impl_fromstr_or_via_f64!(i32, "INT");
impl_fromstr_or_via_f64!(i64, "INT");
impl_fromstr_or_via_f64!(i128, "INT");
impl_fromstr_or_via_f64!(u16, "INT");
impl_fromstr_or_via_f64!(u32, "INT");
impl_fromstr_or_via_f64!(u64, "INT");
impl_fromstr_or_via_f64!(u128, "INT");
impl_fromstr_or_via_f64!(usize, "INT");
macro_rules! impl_from {
($t:ty, $tyname:expr) => {
impl ClapMe for $t {
fn with_clap<T>(info: ArgInfo, app: clap::App,
f: impl FnOnce(clap::App) -> T) -> T {
let conflicts: Vec<_> = info.conflicted_flags.iter().map(AsRef::as_ref).collect();
let ruo: Vec<_> = info.required_unless_one.iter().map(AsRef::as_ref).collect();
if info.name == "" {
f(app.arg(clap::Arg::with_name(info.name)
.takes_value(true)
.value_name($tyname)
.requires_all(info.required_flags)
.required(info.required)
.help(&info.help)))
} else if ruo.len() > 0 {
f(app.arg(clap::Arg::with_name(info.name)
.long(info.name)
.takes_value(true)
.value_name($tyname)
.requires_all(info.required_flags)
.required(info.required)
.conflicts_with_all(&conflicts)
.required_unless_one(&ruo)
.help(&info.help)))
} else {
f(app.arg(clap::Arg::with_name(info.name)
.long(info.name)
.takes_value(true)
.value_name($tyname)
.requires_all(info.required_flags)
.required(info.required)
.conflicts_with_all(&conflicts)
.help(&info.help)))
}
}
fn from_clap(name: &str, matches: &clap::ArgMatches) -> Option<Self> {
matches.value_of(name).map(|s| Self::from(s))
}
}
impl ClapMe for Vec<$t> {
fn with_clap<TT>(info: ArgInfo, app: clap::App,
f: impl FnOnce(clap::App) -> TT) -> TT {
if info.name == "" {
f(app.arg(clap::Arg::with_name(info.name)
.takes_value(true)
.value_name($tyname)
.required(false)
.requires_all(info.required_flags)
.multiple(true)
.help(&info.help)))
} else {
f(app.arg(clap::Arg::with_name(info.name)
.long(info.name)
.takes_value(true)
.value_name($tyname)
.required(false)
.requires_all(info.required_flags)
.multiple(true)
.help(&info.help)))
}
}
fn from_clap(name: &str, matches: &clap::ArgMatches) -> Option<Self> {
Some(matches.values_of(name).unwrap_or(clap::Values::default())
.map(|s| <$t>::from(s)).collect())
}
fn requires_flags(_name: &str) -> Vec<String> {
vec![]
}
}
}
}
impl_from!(String, "STRING");
macro_rules! impl_from_osstr {
($t:ty, $tyname:expr) => {
impl ClapMe for $t {
fn with_clap<T>(info: ArgInfo, app: clap::App,
f: impl FnOnce(clap::App) -> T) -> T {
let conflicts: Vec<_> = info.conflicted_flags.iter().map(AsRef::as_ref).collect();
let ruo: Vec<_> = info.required_unless_one.iter().map(AsRef::as_ref).collect();
if info.name == "" {
f(app.arg(clap::Arg::with_name(info.name)
.takes_value(true)
.value_name($tyname)
.requires_all(info.required_flags)
.required(info.required)
.help(&info.help)))
} else if ruo.len() > 0 {
f(app.arg(clap::Arg::with_name(info.name)
.long(info.name)
.takes_value(true)
.value_name($tyname)
.requires_all(info.required_flags)
.required(info.required)
.conflicts_with_all(&conflicts)
.required_unless_one(&ruo)
.help(&info.help)))
} else {
f(app.arg(clap::Arg::with_name(info.name)
.long(info.name)
.takes_value(true)
.value_name($tyname)
.requires_all(info.required_flags)
.required(info.required)
.conflicts_with_all(&conflicts)
.help(&info.help)))
}
}
fn from_clap(name: &str, matches: &clap::ArgMatches) -> Option<Self> {
matches.value_of_os(name).map(|s| Self::from(s))
}
}
impl ClapMe for Vec<$t> {
fn with_clap<TT>(info: ArgInfo, app: clap::App,
f: impl FnOnce(clap::App) -> TT) -> TT {
if info.name == "" {
f(app.arg(clap::Arg::with_name(info.name)
.takes_value(true)
.value_name($tyname)
.required(false)
.requires_all(info.required_flags)
.multiple(true)
.help(&info.help)))
} else {
f(app.arg(clap::Arg::with_name(info.name)
.long(info.name)
.takes_value(true)
.value_name($tyname)
.required(false)
.requires_all(info.required_flags)
.multiple(true)
.help(&info.help)))
}
}
fn from_clap(name: &str, matches: &clap::ArgMatches) -> Option<Self> {
Some(matches.values_of_os(name).unwrap_or(clap::OsValues::default())
.map(|s| <$t>::from(s)).collect())
}
fn requires_flags(_name: &str) -> Vec<String> {
vec![]
}
}
}
}
impl_from_osstr!(std::path::PathBuf, "PATH");
impl_from_osstr!(std::ffi::OsString, "STRING");
impl<T: ClapMe> ClapMe for Option<T> {
fn with_clap<TT>(mut info: ArgInfo, app: clap::App,
f: impl FnOnce(clap::App) -> TT) -> TT {
info.required = false;
info.required_unless_one = Vec::new();
T::with_clap(info, app, f)
}
fn from_clap(name: &str, matches: &clap::ArgMatches) -> Option<Self> {
Some(T::from_clap(name, matches))
}
fn requires_flags(_name: &str) -> Vec<String> {
vec![]
}
}
impl<T> ClapMe for std::marker::PhantomData<T> {
fn with_clap<TT>(_info: ArgInfo, app: clap::App,
f: impl FnOnce(clap::App) -> TT) -> TT {
f(app)
}
fn from_clap(_name: &str, _matches: &clap::ArgMatches) -> Option<Self> {
Some(std::marker::PhantomData)
}
fn requires_flags(_name: &str) -> Vec<String> {
vec![]
}
}