pub use self::Argument::{Zero, One};
pub use self::Atom::{Short, Long, Command, Positional};
use self::Pattern::{Alternates, Sequence, Optional, Repeat, PatAtom};
use std::borrow::ToOwned;
use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry::{Vacant, Occupied};
use std::cmp::Ordering;
use std::fmt;
use regex;
use regex::Regex;
use dopt::Value::{self, Switch, Counted, Plain, List};
use synonym::SynonymMap;
macro_rules! err(
($($arg:tt)*) => (return Err(format!($($arg)*)))
);
#[derive(Clone)]
pub struct Parser {
pub program: String,
pub full_doc: String,
pub usage: String,
pub descs: SynonymMap<Atom, Options>,
usages: Vec<Pattern>,
last_atom_added: Option<Atom>, }
impl Parser {
pub fn new(doc: &str) -> Result<Parser, String> {
let mut d = Parser {
program: "".to_string(),
full_doc: doc.to_string(),
usage: "".to_string(),
usages: vec!(),
descs: SynonymMap::new(),
last_atom_added: None,
};
try!(d.parse(doc));
Ok(d)
}
pub fn matches(&self, argv: &Argv) -> Option<SynonymMap<String, Value>> {
for usage in self.usages.iter() {
match Matcher::matches(argv, usage) {
None => continue,
Some(vals) => return Some(vals),
}
}
None
}
pub fn parse_argv<'a>(&'a self, argv: Vec<String>, options_first: bool)
-> Result<Argv<'a>, String> {
Argv::new(self, argv, options_first)
}
}
impl Parser {
fn options_atoms(&self) -> Vec<Atom> {
let mut atoms = vec!();
for (atom, _) in self.descs.iter().filter(|&(_, opts)| opts.is_desc) {
atoms.push(atom.clone());
}
atoms
}
fn has_arg(&self, atom: &Atom) -> bool {
match self.descs.find(atom) {
None => false,
Some(opts) => opts.arg.has_arg(),
}
}
fn parse(&mut self, doc: &str) -> Result<(), String> {
let musage = regex!(r"(?is)usage:\s*(?P<prog>\S+)(?P<pats>.*?)(?:$|\n\s*\n)");
let caps = match musage.captures(doc) {
None => err!("Could not find usage patterns in doc string."),
Some(caps) => caps,
};
if caps.name("prog").unwrap_or("").is_empty() {
err!("Could not find program name in doc string.")
}
self.program = caps.name("prog").unwrap_or("").to_string();
self.usage = caps.at(0).unwrap_or("").to_string();
let (pstart, pend) = caps.pos(0).unwrap();
let (before, after) = (&doc[..pstart], &doc[pend..]);
for line in before.lines().chain(after.lines()) {
try!(self.parse_desc(line));
}
let mprog = format!(
"^(?:{})?\\s*(.*?)\\s*$", regex::quote(caps.name("prog").unwrap_or("")));
let pats = Regex::new(&*mprog).unwrap();
if caps.name("pats").unwrap_or("") == "" {
let pattern = try!(
PatParser::new(self, "").parse());
self.usages.push(pattern);
} else {
for line in caps.name("pats").unwrap_or("").lines() {
for pat in pats.captures_iter(line.trim()) {
let pattern = try!(
PatParser::new(self, pat.at(1).unwrap_or("")).parse());
self.usages.push(pattern);
}
}
}
Ok(())
}
fn parse_desc(&mut self, full_desc: &str) -> Result<(), String> {
let desc =
regex!(r"(?i)^\s*options:\s*").replace(full_desc.trim(), "");
let desc = &*desc;
if !regex!(r"^(-\S|--\S)").is_match(desc) {
try!(self.parse_default(full_desc));
return Ok(())
}
let desc = regex!(" .*$").replace(desc, "");
let desc = regex!(r"([^-\s]), -").replace(&*desc, "$1 -");
let desc = desc.trim();
let rflags = regex!("(?:(?P<long>--[^ \t=]+)|(?P<short>-[^ \t=]+))\
(?:(?: |=)(?P<arg>[^-]\\S*))?");
let (mut short, mut long) = ("".to_string(), "".to_string());
let mut has_arg = false;
let mut last_end = 0;
for flags in rflags.captures_iter(desc) {
last_end = flags.pos(0).unwrap().1;
let (s, l) = (
flags.name("short").unwrap_or(""),
flags.name("long").unwrap_or(""),
);
if !s.is_empty() {
if !short.is_empty() {
err!("Only one short flag is allowed in an option \
description, but found '{}' and '{}'.", short, s)
}
short = s.to_string()
}
if !l.is_empty() {
if !long.is_empty() {
err!("Only one long flag is allowed in an option \
description, but found '{}' and '{}'.", long, l)
}
long = l.to_string()
}
if !flags.name("arg").unwrap_or("").is_empty() {
let arg = flags.name("arg").unwrap_or("");
if !Atom::is_arg(arg) {
err!("Argument '{}' is not of the form ARG or <arg>.", arg)
}
has_arg = true; }
}
assert!(last_end <= desc.len());
if last_end < desc.len() {
err!("Extraneous text '{}' in option description '{}'.",
&desc[last_end..], desc)
}
try!(self.add_desc(&*short, &*long, has_arg));
self.parse_default(full_desc)
}
fn parse_default(&mut self, desc: &str) -> Result<(), String> {
let rdefault = regex!(r"(?i)\[default:(?P<val>[^]]*)\]");
let defval =
match rdefault.captures(desc) {
None => return Ok(()),
Some(c) => c.name("val").unwrap_or("").trim(),
};
let last_atom =
match self.last_atom_added {
None => err!("Found default value '{}' in '{}' before first \
option description.", defval, desc),
Some(ref atom) => atom,
};
let opts =
self.descs
.find_mut(last_atom)
.expect(&*format!("BUG: last opt desc key ('{:?}') is invalid.",
last_atom));
match opts.arg {
One(None) => {}, Zero =>
err!("Cannot assign default value '{}' to flag '{}' \
that has no arguments.", defval, last_atom),
One(Some(ref curval)) =>
err!("Flag '{}' already has a default value \
of '{}' (second default value: '{}').",
last_atom, curval, defval),
}
opts.arg = One(Some(defval.to_string()));
Ok(())
}
fn add_desc(&mut self, short: &str, long: &str, has_arg: bool)
-> Result<(), String> {
assert!(!short.is_empty() || !long.is_empty());
if !short.is_empty() && short.chars().count() != 2 {
return Ok(());
}
let mut opts = Options::new(false,
if has_arg { One(None) } else { Zero });
opts.is_desc = true;
if !short.is_empty() && !long.is_empty() {
let (short, long) = (Atom::new(short), Atom::new(long));
self.descs.insert(long.clone(), opts);
self.descs.insert_synonym(short, long.clone());
self.last_atom_added = Some(long);
} else if !short.is_empty() {
let short = Atom::new(short);
self.descs.insert(short.clone(), opts);
self.last_atom_added = Some(short);
} else if !long.is_empty() {
let long = Atom::new(long);
self.descs.insert(long.clone(), opts);
self.last_atom_added = Some(long);
}
Ok(())
}
}
impl fmt::Debug for Parser {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fn sorted<T: Ord>(mut xs: Vec<T>) -> Vec<T> {
xs.sort(); xs
}
try!(writeln!(f, "====="));
try!(writeln!(f, "Program: {}", self.program));
try!(writeln!(f, "Option descriptions:"));
let keys = sorted(self.descs.keys().collect());
for &k in keys.iter() {
try!(writeln!(f, " '{}' => {:?}", k, self.descs.get(k)));
}
try!(writeln!(f, "Synonyms:"));
let keys: Vec<(&Atom, &Atom)> =
sorted(self.descs.synonyms().collect());
for &(from, to) in keys.iter() {
try!(writeln!(f, " {:?} => {:?}", from, to));
}
try!(writeln!(f, "Usages:"));
for pat in self.usages.iter() {
try!(writeln!(f, " {:?}", pat));
}
writeln!(f, "=====")
}
}
struct PatParser<'a> {
dopt: &'a mut Parser,
tokens: Vec<String>, curi: usize, expecting: Vec<char>, }
impl<'a> PatParser<'a> {
fn new(dopt: &'a mut Parser, pat: &str) -> PatParser<'a> {
PatParser {
dopt: dopt,
tokens: pattern_tokens(pat),
curi: 0,
expecting: vec!(),
}
}
fn parse(&mut self) -> Result<Pattern, String> {
let mut p = try!(self.pattern());
match self.expecting.pop() {
None => {},
Some(c) => err!("Unclosed group. Expected '{}'.", c),
}
p.add_options_shortcut(self.dopt);
p.tag_repeats(&mut self.dopt.descs);
Ok(p)
}
fn pattern(&mut self) -> Result<Pattern, String> {
let mut alts = vec!();
let mut seq = vec!();
while !self.is_eof() {
match self.cur() {
"..." => {
err!("'...' must appear directly after a group, argument, \
flag or command.")
}
"-" | "--" => {
seq.push(try!(self.command()));
}
"|" => {
if seq.is_empty() {
err!("Unexpected '|'. Not in form 'a | b | c'.")
}
try!(self.next_noeof("pattern"));
alts.push(Sequence(seq));
seq = vec!();
}
"]" | ")" => {
if seq.is_empty() {
err!("Unexpected '{}'. Empty groups are not allowed.",
self.cur())
}
match self.expecting.pop() {
None => err!("Unexpected '{}'. No open bracket found.",
self.cur()),
Some(c) => {
if c != self.cur().chars().next().unwrap() {
err!("Expected '{}' but got '{}'.",
c, self.cur())
}
}
}
let mk: fn(Vec<Pattern>) -> Pattern =
if self.cur() == "]" { Optional } else { Sequence };
self.next();
return
if alts.is_empty() {
Ok(mk(seq))
} else {
alts.push(Sequence(seq));
Ok(mk(vec!(Alternates(alts))))
}
}
"[" => {
if self.atis(1, "options") && self.atis(2, "]") {
self.next(); self.next(); self.next();
seq.push(self.maybe_repeat(Optional(vec!())));
continue
}
self.expecting.push(']');
seq.push(try!(self.group()));
}
"(" => {
self.expecting.push(')');
seq.push(try!(self.group()));
}
_ => {
if Atom::is_short(self.cur()) {
seq.extend(try!(self.flag_short()).into_iter());
} else if Atom::is_long(self.cur()) {
seq.push(try!(self.flag_long()));
} else if Atom::is_arg(self.cur()) {
seq.push(try!(self.positional()));
} else if Atom::is_cmd(self.cur()) {
seq.push(try!(self.command()));
} else {
err!("Unknown token type '{}'.", self.cur())
}
}
}
}
if alts.is_empty() {
Ok(Sequence(seq))
} else {
alts.push(Sequence(seq));
Ok(Alternates(alts))
}
}
fn flag_short(&mut self) -> Result<Vec<Pattern>, String> {
let mut seq = vec!();
let stacked: String = self.cur()[1..].to_string();
for (i, c) in stacked.chars().enumerate() {
let atom = self.dopt.descs.resolve(&Short(c));
seq.push(PatAtom(atom.clone()));
if !self.dopt.has_arg(&atom) {
self.add_atom_ifnotexists(Zero, &atom);
} else {
let rest = &stacked[i+1..];
if rest.is_empty() {
try!(self.next_flag_arg(&atom));
} else {
try!(self.errif_invalid_flag_arg(&atom, rest));
}
break
}
}
self.next();
if self.atis(0, "...") {
self.next();
seq = seq.into_iter().map(|p| Repeat(Box::new(p))).collect();
}
Ok(seq)
}
fn flag_long(&mut self) -> Result<Pattern, String> {
let (atom, arg) = try!(parse_long_equal(self.cur()));
let atom = self.dopt.descs.resolve(&atom);
if self.dopt.descs.contains_key(&atom) {
let has_arg = self.dopt.has_arg(&atom);
if arg.has_arg() && !has_arg {
err!("Flag '{}' does not take any arguments.", atom)
} else if !arg.has_arg() && has_arg {
try!(self.next_flag_arg(&atom));
}
}
self.add_atom_ifnotexists(arg, &atom);
self.next();
Ok(self.maybe_repeat(PatAtom(atom)))
}
fn next_flag_arg(&mut self, atom: &Atom) -> Result<(), String> {
try!(self.next_noeof(&*format!("argument for flag '{:?}'", atom)));
self.errif_invalid_flag_arg(atom, self.cur())
}
fn errif_invalid_flag_arg(&self, atom: &Atom, arg: &str)
-> Result<(), String> {
if !Atom::is_arg(arg) {
err!("Expected argument for flag '{}', but found \
malformed argument '{}'.", atom, arg)
}
Ok(())
}
fn command(&mut self) -> Result<Pattern, String> {
let atom = Atom::new(self.cur());
self.add_atom_ifnotexists(Zero, &atom);
self.next();
Ok(self.maybe_repeat(PatAtom(atom)))
}
fn positional(&mut self) -> Result<Pattern, String> {
let atom = Atom::new(self.cur());
self.add_atom_ifnotexists(Zero, &atom);
self.next();
Ok(self.maybe_repeat(PatAtom(atom)))
}
fn add_atom_ifnotexists(&mut self, arg: Argument, atom: &Atom) {
if !self.dopt.descs.contains_key(atom) {
let opts = Options::new(false, arg);
self.dopt.descs.insert(atom.clone(), opts);
}
}
fn group(&mut self)
-> Result<Pattern, String> {
try!(self.next_noeof("pattern"));
let pat = try!(self.pattern());
Ok(self.maybe_repeat(pat))
}
fn maybe_repeat(&mut self, pat: Pattern) -> Pattern {
if self.atis(0, "...") {
self.next();
Repeat(Box::new(pat))
} else {
pat
}
}
fn is_eof(&self) -> bool {
self.curi == self.tokens.len()
}
fn next(&mut self) {
if self.curi == self.tokens.len() {
return
}
self.curi += 1;
}
fn next_noeof(&mut self, expected: &str) -> Result<(), String> {
self.next();
if self.curi == self.tokens.len() {
err!("Expected {} but reached end of usage pattern.", expected)
}
Ok(())
}
fn cur<'r>(&'r self) -> &'r str {
&*self.tokens[self.curi]
}
fn atis(&self, offset: usize, is: &str) -> bool {
let i = self.curi + offset;
i < self.tokens.len() && self.tokens[i] == is
}
}
#[derive(Clone, Debug)]
enum Pattern {
Alternates(Vec<Pattern>),
Sequence(Vec<Pattern>),
Optional(Vec<Pattern>),
Repeat(Box<Pattern>),
PatAtom(Atom),
}
#[derive(PartialEq, Eq, Ord, Hash, Clone, Debug)]
pub enum Atom {
Short(char),
Long(String),
Command(String),
Positional(String),
}
#[derive(Clone, Debug)]
pub struct Options {
pub repeats: bool,
pub arg: Argument,
pub is_desc: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Argument {
Zero,
One(Option<String>), }
impl Pattern {
fn add_options_shortcut(&mut self, par: &Parser) {
fn add(pat: &mut Pattern, all_atoms: &HashSet<Atom>, par: &Parser) {
match *pat {
Alternates(ref mut ps) | Sequence(ref mut ps) => {
for p in ps.iter_mut() { add(p, all_atoms, par) }
}
Repeat(ref mut p) => add(&mut **p, all_atoms, par),
PatAtom(_) => {}
Optional(ref mut ps) => {
if !ps.is_empty() {
for p in ps.iter_mut() { add(p, all_atoms, par) }
} else {
for atom in par.options_atoms().into_iter() {
if !all_atoms.contains(&atom) {
ps.push(PatAtom(atom));
}
}
}
}
}
}
let all_atoms = self.all_atoms();
add(self, &all_atoms, par);
}
fn all_atoms(&self) -> HashSet<Atom> {
fn all_atoms(pat: &Pattern, set: &mut HashSet<Atom>) {
match *pat {
Alternates(ref ps) | Sequence(ref ps) | Optional(ref ps) => {
for p in ps.iter() { all_atoms(p, set) }
}
Repeat(ref p) => all_atoms(&**p, set),
PatAtom(ref a) => { set.insert(a.clone()); }
}
}
let mut set = HashSet::new();
all_atoms(self, &mut set);
set
}
fn tag_repeats(&self, map: &mut SynonymMap<Atom, Options>) {
fn dotag(p: &Pattern,
rep: bool,
map: &mut SynonymMap<Atom, Options>,
seen: &mut HashSet<Atom>) {
match *p {
Alternates(ref ps) => {
let fresh = seen.clone();
for p in ps.iter() {
let mut isolated = fresh.clone();
dotag(p, rep, map, &mut isolated);
for a in isolated.into_iter() {
seen.insert(a);
}
}
}
Sequence(ref ps) => {
for p in ps.iter() {
dotag(p, rep, map, seen)
}
}
Optional(ref ps) => {
for p in ps.iter() {
dotag(p, rep, map, seen)
}
}
Repeat(ref p) => dotag(&**p, true, map, seen),
PatAtom(ref atom) => {
let opt = map.find_mut(atom).expect("bug: no atom found");
opt.repeats = opt.repeats || rep || seen.contains(atom);
seen.insert(atom.clone());
}
}
}
let mut seen = HashSet::new();
dotag(self, false, map, &mut seen);
}
}
impl Atom {
pub fn new(s: &str) -> Atom {
if Atom::is_short(s) {
Short(s[1..].chars().next().unwrap())
} else if Atom::is_long(s) {
Long(s[2..].to_string())
} else if Atom::is_arg(s) {
if s.starts_with("<") && s.ends_with(">") {
Positional(s[1..s.len()-1].to_string())
} else {
Positional(s.to_string())
}
} else if Atom::is_cmd(s) {
Command(s.to_string())
} else {
panic!("Unknown atom string: '{}'", s)
}
}
fn is_short(s: &str) -> bool { regex!(r"^-[^-]+$").is_match(s) }
fn is_long(s: &str) -> bool { regex!(r"^--\S+(?:<[^>]+>)?$").is_match(s) }
fn is_arg(s: &str) -> bool { regex!(r"^(\p{Lu}+|<[^>]+>)$").is_match(s) }
fn is_cmd(s: &str) -> bool { regex!(r"^(-|--|[^-]\S*)$").is_match(s) }
fn type_as_usize(&self) -> usize {
match *self {
Short(_) => 0,
Long(_) => 1,
Command(_) => 2,
Positional(_) => 3,
}
}
}
impl PartialOrd for Atom {
fn partial_cmp(&self, other: &Atom) -> Option<Ordering> {
match (self, other) {
(&Short(c1), &Short(c2)) => c1.partial_cmp(&c2),
(&Long(ref s1), &Long(ref s2)) => s1.partial_cmp(s2),
(&Command(ref s1), &Command(ref s2)) => s1.partial_cmp(s2),
(&Positional(ref s1), &Positional(ref s2)) => s1.partial_cmp(s2),
(a1, a2) => a1.type_as_usize().partial_cmp(&a2.type_as_usize()),
}
}
}
impl fmt::Display for Atom {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Short(c) => write!(f, "-{}", c),
Long(ref s) => write!(f, "--{}", s),
Command(ref s) => write!(f, "{}", s),
Positional(ref s) => {
if s.chars().all(|c| c.is_uppercase()) {
write!(f, "{}", s)
} else {
write!(f, "<{}>", s)
}
}
}
}
}
impl Options {
fn new(rep: bool, arg: Argument) -> Options {
Options { repeats: rep, arg: arg, is_desc: false, }
}
}
impl Argument {
fn has_arg(&self) -> bool {
match *self {
Zero => false,
One(_) => true,
}
}
}
#[doc(hidden)]
pub struct Argv<'a> {
positional: Vec<ArgvToken>,
flags: Vec<ArgvToken>,
counts: HashMap<Atom, usize>,
dopt: &'a Parser,
argv: Vec<String>,
curi: usize,
options_first: bool,
}
#[derive(Clone, Debug)]
struct ArgvToken {
atom: Atom,
arg: Option<String>,
}
impl<'a> Argv<'a> {
fn new(dopt: &'a Parser, argv: Vec<String>, options_first: bool)
-> Result<Argv<'a>, String> {
let mut a = Argv {
positional: vec!(),
flags: vec!(),
counts: HashMap::new(),
dopt: dopt,
argv: argv.iter().map(|s| s.to_string()).collect(),
curi: 0,
options_first: options_first,
};
try!(a.parse());
for flag in a.flags.iter() {
match a.counts.entry(flag.atom.clone()) {
Vacant(v) => { v.insert(1); }
Occupied(mut v) => { *v.get_mut() += 1; }
}
}
Ok(a)
}
fn parse(&mut self) -> Result<(), String> {
let mut seen_double_dash = false;
while self.curi < self.argv.len() {
let do_flags =
!seen_double_dash
&& (!self.options_first || self.positional.is_empty());
if do_flags && Atom::is_short(self.cur()) {
let stacked: String = self.cur()[1..].to_string();
for (i, c) in stacked.chars().enumerate() {
let mut tok = ArgvToken {
atom: self.dopt.descs.resolve(&Short(c)),
arg: None,
};
if !self.dopt.descs.contains_key(&tok.atom) {
err!("Unknown flag: '{}'", &tok.atom)
}
if !self.dopt.has_arg(&tok.atom) {
self.flags.push(tok);
} else {
let rest = &stacked[i+1..];
tok.arg = Some(
if rest.is_empty() {
let arg = try!(self.next_arg(&tok.atom));
arg.to_string()
} else {
rest.to_string()
}
);
self.flags.push(tok);
break
}
}
} else if do_flags && Atom::is_long(self.cur()) {
let (atom, mut arg) = parse_long_equal_argv(self.cur());
let atom = self.dopt.descs.resolve(&atom);
if !self.dopt.descs.contains_key(&atom) {
err!("Unknown flag: '{}'", &atom)
}
if arg.is_some() && !self.dopt.has_arg(&atom) {
err!("Flag '{}' cannot have an argument, but found '{:?}'.",
&atom, &arg)
} else if arg.is_none() && self.dopt.has_arg(&atom) {
try!(self.next_noeof(&*format!("argument for flag '{:?}'",
&atom)));
arg = Some(self.cur().to_string());
}
self.flags.push(ArgvToken { atom: atom, arg: arg });
} else {
if self.cur() == "--" {
seen_double_dash = true;
} else {
let tok = ArgvToken {
atom: Positional(self.cur().to_string()),
arg: None,
};
self.positional.push(tok);
}
}
self.next()
}
Ok(())
}
fn cur<'b>(&'b self) -> &'b str { self.at(0) }
fn at<'b>(&'b self, i: usize) -> &'b str {
&*self.argv[self.curi + i]
}
fn next(&mut self) {
if self.curi < self.argv.len() {
self.curi += 1
}
}
fn next_arg<'b>(&'b mut self, atom: &Atom) -> Result<&'b str, String> {
let expected = format!("argument for flag '{:?}'", atom);
try!(self.next_noeof(&*expected));
Ok(self.cur())
}
fn next_noeof(&mut self, expected: &str) -> Result<(), String> {
self.next();
if self.curi == self.argv.len() {
err!("Expected {} but reached end of arguments.", expected)
}
Ok(())
}
}
impl<'a> fmt::Debug for Argv<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
try!(writeln!(f, "Positional: {:?}", self.positional));
try!(writeln!(f, "Flags: {:?}", self.flags));
try!(writeln!(f, "Counts: {:?}", self.counts));
Ok(())
}
}
struct Matcher<'a, 'b:'a> {
argv: &'a Argv<'b>,
}
#[derive(Clone, Debug, PartialEq)]
struct MState {
argvi: usize, counts: HashMap<Atom, usize>, max_counts: HashMap<Atom, usize>, vals: HashMap<Atom, Value>,
}
impl MState {
fn fill_value(&mut self, key: Atom, rep: bool, arg: Option<String>)
-> bool {
match (arg, rep) {
(None, false) => {
self.vals.insert(key, Switch(true));
}
(Some(arg), false) => {
self.vals.insert(key, Plain(Some(arg)));
}
(None, true) => {
match self.vals.entry(key) {
Vacant(v) => { v.insert(Counted(1)); }
Occupied(mut v) => {
match *v.get_mut() {
Counted(ref mut c) => { *c += 1; }
_ => return false,
}
}
}
}
(Some(arg), true) => {
match self.vals.entry(key) {
Vacant(v) => { v.insert(List(vec!(arg))); }
Occupied(mut v) => {
match *v.get_mut() {
List(ref mut vs) => vs.push(arg),
_ => return false,
}
}
}
}
}
true
}
fn add_value(&mut self, opts: &Options,
spec: &Atom, atom: &Atom, arg: &Option<String>) -> bool {
assert!(opts.arg.has_arg() == arg.is_some(),
"'{:?}' should have an argument but doesn't", atom);
match *atom {
Short(_) | Long(_) => {
self.fill_value(spec.clone(), opts.repeats, arg.clone())
}
Positional(ref v) => {
assert!(!opts.arg.has_arg());
self.fill_value(spec.clone(), opts.repeats, Some(v.clone()))
}
Command(_) => {
assert!(!opts.arg.has_arg());
self.fill_value(spec.clone(), opts.repeats, None)
}
}
}
fn use_flag(&mut self, flag: &Atom) -> bool {
match self.max_counts.entry(flag.clone()) {
Vacant(v) => { v.insert(0); }
Occupied(_) => {}
}
match self.counts.entry(flag.clone()) {
Vacant(_) => { false }
Occupied(mut v) => {
let c = v.get_mut();
if *c == 0 {
false
} else {
*c -= 1;
true
}
}
}
}
fn use_optional_flag(&mut self, flag: &Atom) {
match self.max_counts.entry(flag.clone()) {
Vacant(v) => { v.insert(1); }
Occupied(mut v) => { *v.get_mut() += 1; }
}
}
fn match_cmd_or_posarg(&mut self, spec: &Atom, argv: &ArgvToken)
-> Option<ArgvToken> {
match (spec, &argv.atom) {
(_, &Command(_)) => {
unreachable!()
}
(&Command(ref n1), &Positional(ref n2)) if n1 == n2 => {
self.argvi += 1;
Some(ArgvToken { atom: spec.clone(), arg: None })
}
(&Positional(_), _) => {
self.argvi += 1;
Some(argv.clone())
}
_ => None,
}
}
}
impl<'a, 'b> Matcher<'a, 'b> {
fn matches(argv: &'a Argv, pat: &Pattern)
-> Option<SynonymMap<String, Value>> {
let m = Matcher { argv: argv };
let init = MState {
argvi: 0,
counts: argv.counts.clone(),
max_counts: HashMap::new(),
vals: HashMap::new(),
};
m.states(pat, &init)
.into_iter()
.filter(|s| m.state_consumed_all_argv(s))
.filter(|s| m.state_has_valid_flags(s))
.filter(|s| m.state_valid_num_flags(s))
.collect::<Vec<MState>>()
.into_iter()
.next()
.map(|mut s| {
m.add_flag_values(&mut s);
m.add_default_values(&mut s);
let mut synmap: SynonymMap<String, Value> =
s.vals.into_iter()
.map(|(k, v)| (k.to_string(), v))
.collect();
for (from, to) in argv.dopt.descs.synonyms() {
let (from, to) = (from.to_string(), to.to_string());
if synmap.contains_key(&to) {
synmap.insert_synonym(from, to);
}
}
synmap
})
}
fn token_from(&'a self, state: &MState) -> Option<&'a ArgvToken> {
self.argv.positional.get(state.argvi)
}
fn add_value(&self, state: &mut MState,
atom_spec: &Atom, atom: &Atom, arg: &Option<String>)
-> bool {
let opts = self.argv.dopt.descs.get(atom_spec);
state.add_value(opts, atom_spec, atom, arg)
}
fn add_flag_values(&self, state: &mut MState) {
for tok in self.argv.flags.iter() {
self.add_value(state, &tok.atom, &tok.atom, &tok.arg);
}
}
fn add_default_values(&self, state: &mut MState) {
let vs = &mut state.vals;
for (a, opts) in self.argv.dopt.descs.iter() {
if vs.contains_key(a) {
continue
}
let atom = a.clone();
match (opts.repeats, &opts.arg) {
(false, &Zero) => {
match a {
&Positional(_) => vs.insert(atom, Plain(None)),
_ => vs.insert(atom, Switch(false)),
};
}
(true, &Zero) => {
match a {
&Positional(_) => vs.insert(atom, List(vec!())),
_ => vs.insert(atom, Counted(0)),
};
}
(false, &One(None)) => { vs.insert(atom, Plain(None)); }
(true, &One(None)) => { vs.insert(atom, List(vec!())); }
(false, &One(Some(ref v))) => {
vs.insert(atom, Plain(Some(v.clone())));
}
(true, &One(Some(ref v))) => {
let words = regex!(r"\s+")
.split(v)
.map(|s| s.to_owned())
.collect();
vs.insert(atom, List(words));
}
}
}
}
fn state_consumed_all_argv(&self, state: &MState) -> bool {
self.argv.positional.len() == state.argvi
}
fn state_has_valid_flags(&self, state: &MState) -> bool {
self.argv.counts.keys().all(|flag| state.max_counts.contains_key(flag))
}
fn state_valid_num_flags(&self, state: &MState) -> bool {
state.counts.iter().all(
|(flag, count)| count <= &state.max_counts[flag])
}
fn states(&self, pat: &Pattern, init: &MState) -> Vec<MState> {
match *pat {
Alternates(ref ps) => {
let mut alt_states = vec!();
for p in ps.iter() {
alt_states.extend(self.states(p, init).into_iter());
}
alt_states
}
Sequence(ref ps) => {
let (mut states, mut next) = (vec!(), vec!());
let mut iter = ps.iter();
match iter.next() {
None => return vec!(init.clone()),
Some(p) => states.extend(self.states(p, init).into_iter()),
}
for p in iter {
for s in states.into_iter() {
next.extend(self.states(p, &s).into_iter());
}
states = vec!();
states.extend(next.into_iter());
next = vec!();
}
states
}
Optional(ref ps) => {
let mut base = init.clone();
let mut noflags = vec!();
for p in ps.iter() {
match p {
&PatAtom(ref a @ Short(_))
| &PatAtom(ref a @ Long(_)) => {
let argv_count = self.argv.counts.get(a)
.map(|&x|x).unwrap_or(0);
let max_count = base.max_counts.get(a)
.map(|&x|x).unwrap_or(0);
if argv_count > max_count {
base.use_optional_flag(a);
}
}
other => {
noflags.push(other);
}
}
}
let mut states = vec!();
self.all_option_states(&base, &mut states, &*noflags);
states
}
Repeat(ref p) => {
let mut grouped_states = vec!(self.states(&**p, init));
loop {
let mut nextss = vec!();
for s in grouped_states.last().unwrap().iter() {
nextss.extend(
self.states(&**p, s)
.into_iter()
.filter(|snext| snext != s));
}
if nextss.is_empty() {
break
}
grouped_states.push(nextss);
}
grouped_states
.into_iter()
.flat_map(|ss| ss.into_iter())
.collect::<Vec<MState>>()
}
PatAtom(ref atom) => {
let mut state = init.clone();
match *atom {
Short(_) | Long(_) => {
if !state.use_flag(atom) {
return vec!()
}
}
Command(_) | Positional(_) => {
let tok =
match self.token_from(init) {
None => return vec!(),
Some(tok) => tok,
};
let tok =
match state.match_cmd_or_posarg(atom, tok) {
None => return vec!(),
Some(tok) => tok,
};
if !self.add_value(&mut state, atom,
&tok.atom, &tok.arg) {
return vec!()
}
}
}
vec!(state)
}
}
}
fn all_option_states(&self, base: &MState, states: &mut Vec<MState>,
pats: &[&Pattern]) {
if pats.is_empty() {
states.push(base.clone());
} else {
let (pat, rest) = (*pats.first().unwrap(), &pats[1..]);
for s in self.states(pat, base).into_iter() {
self.all_option_states(&s, states, rest);
}
self.all_option_states(base, states, &pats[1..]);
}
}
}
fn parse_long_equal(flag: &str) -> Result<(Atom, Argument), String> {
let long_equal = regex!("^(?P<name>[^=]+)=(?P<arg>.+)$");
match long_equal.captures(flag) {
None => Ok((Atom::new(flag), Zero)),
Some(cap) => {
let arg = cap.name("arg").unwrap_or("").to_string();
if !Atom::is_arg(&*arg) {
err!("Argument '{}' for flag '{}' is not in the \
form ARG or <arg>.", flag, arg)
}
Ok((Atom::new(cap.name("name").unwrap_or("")), One(None)))
}
}
}
fn parse_long_equal_argv(flag: &str) -> (Atom, Option<String>) {
let long_equal = regex!("^(?P<name>[^=]+)=(?P<arg>.*)$");
match long_equal.captures(flag) {
None => (Atom::new(flag), None),
Some(cap) => (
Atom::new(cap.name("name").unwrap_or("")),
Some(cap.name("arg").unwrap_or("").to_string()),
),
}
}
fn pattern_tokens(pat: &str) -> Vec<String> {
let rpat = regex!(r"\.\.\.|\[|\]|\(|\)|\|");
let rwords = regex!(r"--\S+?=<[^>]+>|<[^>]+>|\S+");
let pat = rpat.replace_all(pat.trim(), " $0 ");
let mut words = vec!();
for cap in rwords.captures_iter(&*pat) {
words.push(cap.at(0).unwrap_or("").to_string());
}
words
}