use std::{ffi::OsString, fmt, str::FromStr}; use crate::{Error, Result}; macro_rules! format_err { ($($tt:tt)*) => { Error { msg: format!($($tt)*), help: false } }; } macro_rules! bail { ($($tt:tt)*) => { return Err(format_err!($($tt)*)) }; } pub struct Parser { after_double_dash: bool, rargs: Vec, } impl Parser { pub fn new(mut args: Vec) -> Self { args.reverse(); Self { after_double_dash: false, rargs: args } } pub fn new_from_env() -> Self { let args = std::env::args_os().collect::>(); let mut res = Parser::new(args); let _progn = res.next(); res } pub fn pop_flag(&mut self) -> Option> { if self.after_double_dash { self.next().map(Err) } else { let arg = self.next()?; let arg_str = arg.to_str().unwrap_or_default(); if arg_str.starts_with('-') { if arg_str == "--" { self.after_double_dash = true; return self.next().map(Err); } Some(arg.into_string()) } else { Some(Err(arg)) } } } pub fn push_back(&mut self, arg: Result) { let arg = match arg { Ok(it) => it.into(), Err(it) => it, }; self.rargs.push(arg) } fn next(&mut self) -> Option { self.rargs.pop() } pub fn next_value(&mut self, flag: &str) -> Result { self.next().ok_or_else(|| format_err!("expected a value for `{flag}`")) } pub fn next_value_from_str(&mut self, flag: &str) -> Result where T::Err: fmt::Display, { let value = self.next_value(flag)?; self.value_from_str(flag, value) } pub fn value_from_str(&mut self, flag: &str, value: OsString) -> Result where T::Err: fmt::Display, { match value.into_string() { Ok(str) => str.parse::().map_err(|err| format_err!("can't parse `{flag}`, {err}")), Err(it) => { bail!("can't parse `{flag}`, invalid utf8: {it:?}") } } } pub fn unexpected_flag(&self, flag: &str) -> Error { format_err!("unexpected flag: `{flag}`") } pub fn unexpected_arg(&self, arg: OsString) -> Error { format_err!("unexpected argument: {arg:?}") } pub fn subcommand_required(&self) -> Error { format_err!("subcommand is required") } pub fn help(&self, help: &'static str) -> Error { Error { msg: help.to_string(), help: true } } pub fn optional(&self, flag: &str, mut vals: Vec) -> Result> { if vals.len() > 1 { bail!("flag specified more than once: `{flag}`") } Ok(vals.pop()) } pub fn required(&self, flag: &str, mut vals: Vec) -> Result { if vals.len() > 1 { bail!("flag specified more than once: `{flag}`") } vals.pop().ok_or_else(|| format_err!("flag is required: `{flag}`")) } }