1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
#![deny(missing_docs)]
//! Parse options from command line arguments
#[derive(PartialEq, Eq)]
/// An option long form rule.
///
/// `DoubleDash` allows `--foo`.
/// `Both` allows both `-foo` and `--foo`.
pub enum LongForm {
/// Allows the `-foo` long option form.
SimpleDash,
/// Allows the `--foo` long option form.
DoubleDash,
/// Allows both the `--foo` and `-foo` long option form.
Both,
}
/// A macro used to generate `Opt`s.
///
/// Usage:
/// - `opt!('c' /* Short form and identifier */)`
/// - `opt!('c', "ctrl" /* Long form */)`
/// - `opt!('c', "ctrl", LongForm::SimpleDash /* Long rule */)`
/// - `opt!('c', "ctrl", LongForm::SimpleDash, true /* No short flag */)`
///
/// If not specified, the fields are set to their defaults.
#[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,
}
};
}
/// A command line option rule.
///
/// At least one option form must be present, `short` and `long` cannot both be `None`.
pub struct Opt<'a> {
/// The long-option rule, telling how long options should be parsed.
///
/// Default: `LongForm::DoubleDash`.
pub long_rule: LongForm,
/// The short form and identifier for the option.
pub short: char,
/// The long form for the option.
///
/// Default: `None`.
pub long: Option<&'a str>,
/// Disallow the short form.
///
/// Default: `false`.
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]
}
}
}
/// Get command line options.
///
/// `args` is the vector containing the command line arguments.
/// `optstring` is a string reference, like `ab:c?`, `ab` or `hVv`.
/// > Each alphanumeric character is an option, and the following `:` and `?` respectively mean
/// > that it takes a mandatory or optional value.
///
/// `opts` are the option rules.
///
/// If no matching option is found, `None` is returned.
/// If a mandatory value is not given, an error message is displayed and `Some(('?', None))` is
/// returned.
/// If the option doesn't take an argument or if an optional argument is not given, `Some((opt,
/// None))` is returned.
///
/// ### Example
///
/// ```no_run
/// #[macro_use]
/// extern crate getopt_rs;
///
/// use std::env;
/// use getopt_rs::{getopt, LongForm};
///
/// fn main() {
/// let mut args = env::args().collect();
/// while let Some(opt) = getopt(&mut args,
/// "ab?c:",
/// &[
/// opt!('a'),
/// opt!('b', "bar"),
/// opt!('c', "ctrl", LongForm::SimpleDash)])
/// {
/// match opt {
/// ('a', _) => println!("Found option 'a' that takes no argument."),
/// ('b', val) => println!("Found option 'b' that takes an optional argument: {:?}.", val),
/// ('c', val) => println!("Found option 'c' that takes a mandatory argument: {:?}", val.unwrap()),
/// _ => return, /* An error occured, Some(('?', None)) is returned. */
/// }
/// }
/// }
/// ```
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));
}