use {
std::io::{Error, ErrorKind},
crate::Result,
super::DASH,
};
#[derive(Debug, Eq, PartialEq)]
pub (super) enum Kind {
Command,
ShortOption,
ShortOptionWithValue {
option: String,
value: String,
},
LongOption,
LongOptionWithValue {
option: String,
value: String,
},
SubArgsSeparator,
}
impl Kind {
pub fn parse(s: &str) -> Result<Self> {
let mut chars = s.chars().peekable();
if chars.next() == Some(DASH) {
if chars.next_if_eq(&DASH).is_some() {
if chars.peek().is_none() {
return Ok(Self::SubArgsSeparator);
}
return parse_long_options(&mut chars).ok_or_else(|| Error::new(ErrorKind::InvalidData, format!("Invalid long option: {s:?}")));
}
return parse_short_options(&mut chars).ok_or_else(|| Error::new(ErrorKind::InvalidData, format!("Invalid short option: {s:?}")));
}
Ok(Self::Command)
}
}
fn is_option_char(c: char) -> bool {
matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9')
}
fn parse_long_options<I>(chars: &mut I) -> Option<Kind> where I: Iterator<Item=char> {
let mut option = String::with_capacity(chars.size_hint().0);
match chars.next() {
Some(c) if is_option_char(c) => option.push(c),
_ => return None,
};
while let Some(c) = chars.next() {
if is_option_char(c) {
option.push(c);
continue;
}
match c {
'-' => {
option.push(c);
continue;
},
'=' => {
option.insert(usize::MIN, DASH);
option.insert(usize::MIN, DASH);
return Some(Kind::LongOptionWithValue {
option,
value: chars.collect(),
});
},
_ => return None,
};
}
Some(Kind::LongOption)
}
fn parse_short_options<I>(chars: &mut I) -> Option<Kind> where I: Iterator<Item=char> {
match chars.next() {
Some(c) if is_option_char(c) => match chars.next() {
None => Some(Kind::ShortOption),
Some('=') => Some(Kind::ShortOptionWithValue {
option: [DASH, c].into_iter().collect(),
value: chars.collect(),
}),
Some(_) => None,
},
_ => None,
}
}