use crate::bin_error::{BinError, BinResult};
use std::{collections::HashMap, env};
pub type OptList = HashMap<String, Option<String>>;
pub type ArgList = Vec<String>;
pub trait CmdOpt {
fn get_opt(
&mut self,
opt_name: &str,
need_opt_value: bool,
) -> BinResult<(bool, Option<String>)>;
fn check_if_empty(&self) -> BinResult<()>;
}
impl CmdOpt for OptList {
fn get_opt(
&mut self,
opt_name: &str,
need_opt_value: bool,
) -> BinResult<(bool, Option<String>)> {
self.remove(opt_name) .or(opt_name.get(..1).and_then(|c| self.remove(c))) .map_or(
Ok((false, None)),
|v| {
(v.is_some() == need_opt_value).then(|| (true, v)).ok_or(
BinError::InvalidInput(format!(
"Invalid argument: Argument '{}' {} option value!",
opt_name,
match need_opt_value {
true => "needs an",
false => "must not have any",
}
)),
)
},
)
}
fn check_if_empty(&self) -> BinResult<()> {
match self.iter().next() {
Some(remaining_opt) => Err(BinError::InvalidInput(format!(
"Invalid option: '{}'",
remaining_opt.0
))),
_ => Ok(()),
}
}
}
pub fn parse_args() -> BinResult<(ArgList, OptList)> {
let mut args = env::args();
let _bin_name = args.next();
args.fold(
Ok(((ArgList::new(), OptList::new()), None, false)),
|list_wrapper, arg| {
list_wrapper.and_then(|(mut list, mut last_opt_name, opt_mode)| {
match (
arg.to_string(),
arg.split_once('-')
.filter(|s| s.0.is_empty() && (!s.1.is_empty())),
) {
(_, Some(optstr)) => {
match (
optstr.1.to_string(),
optstr.1.split_once('-').filter(|s| {
s.0.is_empty() && {
s.1.chars().next().filter(|c| *c != '=').is_some()
}
}),
) {
(_, Some(("", longopt))) => match longopt.find('=') {
Some(pos) => {
longopt
.get(..pos)
.zip(longopt.get((pos + 1)..))
.map(|(k, v)| (k.to_string(), Some(v.to_string())))
}
_ => {
last_opt_name = Some(longopt.to_string());
Some((longopt.to_string(), None))
}
},
(shortopt, _) => match shortopt.split_at(1) {
(k, "") => {
last_opt_name = Some(shortopt.to_string());
Some((k.to_string(), None))
}
(k, v) => {
Some((k.to_string(), Some(v.to_string())))
}
},
}
.map(|(k, v)| list.1.insert(k, v))
.or_else(|| panic!("Unexpected error on parsing arguments!"));
Ok((list, last_opt_name, true))
}
(fullstr, _) => {
last_opt_name.as_ref().and_then(|opt_name| {
list.1.insert(opt_name.clone(), Some(fullstr.clone()))
});
match opt_mode {
true => {
last_opt_name.ok_or(BinError::InvalidInput(format!(
"Invalid argument: '{}'",
fullstr
)))?;
}
false => {
list.0.push(fullstr.clone());
}
}
Ok((list, None, opt_mode))
}
}
})
},
)
.map(|(list, _, _)| list)
}