clap/src/usageparser.rs

136 lines
4.5 KiB
Rust
Raw Normal View History

use std::str::Chars;
pub enum UsageToken<'u> {
Name(&'u str, Option<bool>),
Short(char),
Long(&'u str),
Help(&'u str),
Multiple,
}
pub struct UsageParser<'u> {
usage: &'u str,
chars: Chars<'u>,
s: usize,
e: usize,
}
impl<'u> UsageParser<'u> {
pub fn with_usage(u: &'u str) -> UsageParser<'u> {
UsageParser {
usage: u,
chars: u.chars(),
s: 0,
e: 0,
}
}
}
impl<'u> Iterator for UsageParser<'u> {
type Item = UsageToken<'u>;
fn next(&mut self) -> Option<UsageToken<'u>> {
loop {
match self.chars.next() {
Some(c) if c == '[' || c == '<' => {
// self.s = self.e + 1;
if self.e != 0 {
self.e += 1;
}
self.s = self.e + 1;
let closing = match c {
'[' => ']',
'<' => '>',
_ => unreachable!()
};
while let Some(c) = self.chars.next() {
self.e += 1;
if c == closing { break }
}
if self.e > self.usage.len() { return None }
// self.e += 1;
let name = &self.usage[self.s..self.e];
return Some(UsageToken::Name(name, if c == '<' { Some(true)} else {None}));
},
Some('\'') => {
self.s = self.e + 2;
self.e = self.usage.len() - 1;
while let Some(_) = self.chars.next() { continue }
return Some(UsageToken::Help(&self.usage[self.s..self.e]));
},
Some('-') => {
self.e += 1;
match self.chars.next() {
Some('-') => {
if self.e != 1 {
self.e += 1;
}
self.s = self.e + 1;
while let Some(c) = self.chars.next() {
self.e += 1;
if c == ' ' || c == '=' || c == '.' { break }
}
if self.e > self.usage.len() { return None }
if self.e == self.usage.len() - 1 {
return Some(UsageToken::Long(&self.usage[self.s..]))
}
return Some(UsageToken::Long(&self.usage[self.s..self.e]))
},
Some(c) => {
// When short is first don't increment e
if self.e != 1 {
self.e += 1;
}
// Short
if !c.is_alphanumeric() {
return None
}
return Some(UsageToken::Short(c))
},
_ => {
return None
}
}
},
Some('.') => {
self.e += 1;
let mut mult = false;
for _ in 0..2 {
self.e += 1;
match self.chars.next() {
// longs consume one '.' so they match '.. ' whereas shorts can match '...'
Some('.') | Some(' ') => { mult = true; },
_ => {
// if there is no help or following space all we can match is '..'
if self.e == self.usage.len() - 1 {
mult = true;
}
break;
}
}
}
if mult { return Some(UsageToken::Multiple) }
},
Some(' ') | Some('=') | Some(']') | Some('>') | Some('\t') => {
self.e += 1;
continue
},
_ => {
return None
}
}
}
}
}