#![deny(missing_docs)]
#[derive(PartialEq, Eq)]
pub enum LongForm {
SimpleDash,
DoubleDash,
Both,
}
#[macro_export]
macro_rules! opt {
($short:literal) => {
$crate::Opt {
long_rule: $crate::LongForm::DoubleDash,
short: $short,
long: None,
no_short: false,
}
};
($short:literal, $long:literal) => {
$crate::Opt {
long_rule: $crate::LongForm::DoubleDash,
short: $short,
long: Some($long),
no_short: false,
}
};
($short:literal, $long:literal, $long_rule:expr) => {
$crate::Opt {
long_rule: $long_rule,
short: $short,
long: Some($long),
no_short: false,
}
};
($short:literal, $long:literal, $long_rule:expr, $no_short:expr) => {
$crate::Opt {
long_rule: $long_rule,
short: $short,
long: Some($long),
no_short: $no_short,
}
};
}
pub struct Opt<'a> {
pub long_rule: LongForm,
pub short: char,
pub long: Option<&'a str>,
pub no_short: bool,
}
impl<'a> Opt<'a> {
pub(crate) fn gen_long(&self) -> Vec<String> {
let short = format!("-{}", self.short);
if let Some((slong, dlong)) = self
.long
.and_then(|s| Some((format!("-{}", s), format!("--{}", s))))
{
let mut rules = vec![];
if self.long_rule == LongForm::Both {
rules.push(slong);
rules.push(dlong);
} else if self.long_rule == LongForm::SimpleDash {
rules.push(slong);
} else {
rules.push(dlong);
}
if !self.no_short {
rules.push(short);
}
rules
} else {
vec![short]
}
}
}
pub fn getopt(
args: &mut Vec<String>,
optstring: &str,
opts: &[Opt],
) -> Option<(char, Option<String>)> {
opts.iter()
.map(|opt| assert!(!(opt.no_short && opt.long.is_none())))
.count();
let mut optchars = optstring.chars();
while let Some(c) = optchars.next() {
if let Some(forms) = opts
.iter()
.find(|opt| opt.short == c)
.and_then(|opt| Some(opt.gen_long()))
{
for form in forms {
if let Some(idx) = args.iter().position(|a| a == &form) {
args.remove(idx);
return procopt(args, c, idx, optchars.next());
}
}
}
}
None
}
fn procopt(
args: &mut Vec<String>,
c: char,
idx: usize,
next: Option<char>,
) -> Option<(char, Option<String>)> {
let value = if let Some(n) = next {
let available = idx < args.len();
if !available {
if n == ':' {
eprintln!("{}: Option '{}' requires an argument.", args[0], c);
return Some(('?', None));
} else {
None
}
} else if n == ':' || (n == '?' && available) {
let v = args[idx].clone();
args.remove(idx);
Some(v)
} else {
None
}
} else {
None
};
return Some((c, value));
}