|
|
@ -18,6 +18,21 @@ pub enum Kind<'a>
|
|
|
|
Short(&'a [(char, )], bool),
|
|
|
|
Short(&'a [(char, )], bool),
|
|
|
|
/// A single `--`.
|
|
|
|
/// A single `--`.
|
|
|
|
Terminator,
|
|
|
|
Terminator,
|
|
|
|
|
|
|
|
/// An exact match with no prefix.
|
|
|
|
|
|
|
|
Exact(&'a str),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Kind<'static>
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pub fn as_ref<'a>(&'a self) -> Kind<'a>
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
match &self {
|
|
|
|
|
|
|
|
Self::Long(strs, bl) => Kind::Long(strs, *bl),
|
|
|
|
|
|
|
|
Self::Short(chrs, bl) => Kind::Short(chrs, *bl),
|
|
|
|
|
|
|
|
Self::Terminator => Kind::Terminator,
|
|
|
|
|
|
|
|
Self::Exact(stri) => Kind::Exact(&stri[..]),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
@ -33,25 +48,25 @@ pub struct ArgState<T=()>
|
|
|
|
{
|
|
|
|
{
|
|
|
|
user: T,
|
|
|
|
user: T,
|
|
|
|
|
|
|
|
|
|
|
|
flags: Vec<(Kind<'static>, Cow<'static, str>, ValueKind, Box<(dyn FlagCallback<T, Box<dyn Any + 'static>, Box<dyn error::Error + 'static>> + 'static)>, bool, bool)>,
|
|
|
|
flags: Vec<(Kind<'static>, Cow<'static, str>, ValueKind, Box<(dyn FlagCallback<T, Box<dyn Any + 'static>, eyre::Report> + 'static)>, bool, bool)>,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub trait FlagCallback<U, T,E>: FnMut(&mut StateHandle<'_, U>, usize, &str) -> Result<T, E>{}
|
|
|
|
pub trait FlagCallback<U, T,E>: FnMut(&mut StateHandle<'_, U>, usize, &str) -> Result<T, E>{}
|
|
|
|
impl<U, F, T, E> FlagCallback<U, T,E> for F
|
|
|
|
impl<U, F, T, E> FlagCallback<U, T,E> for F
|
|
|
|
where F: FnMut(&mut StateHandle<'_, U>, usize, &str) -> Result<T, E>,
|
|
|
|
where F: FnMut(&mut StateHandle<'_, U>, usize, &str) -> Result<T, E>,
|
|
|
|
T: Any + 'static,
|
|
|
|
T: Any + 'static,
|
|
|
|
E: 'static{}
|
|
|
|
E: Into<eyre::Report> {}
|
|
|
|
|
|
|
|
|
|
|
|
impl<U> ArgState<U>
|
|
|
|
impl<U> ArgState<U>
|
|
|
|
{
|
|
|
|
{
|
|
|
|
/// Push a handle to the state
|
|
|
|
/// Push a handle to the state
|
|
|
|
pub fn push<T: Any + 'static,E: error::Error + 'static, F>(&mut self, kind: Kind<'static>, desc: impl Into<Cow<'static, str>>, value: ValueKind, mut handle: F, single: bool) -> usize
|
|
|
|
pub fn push<T: Any + 'static,E: Into<eyre::Report>, F>(&mut self, kind: Kind<'static>, desc: impl Into<Cow<'static, str>>, value: ValueKind, mut handle: F, single: bool) -> usize
|
|
|
|
where F: FlagCallback<U, T,E> + 'static
|
|
|
|
where F: FlagCallback<U, T,E> + 'static
|
|
|
|
{
|
|
|
|
{
|
|
|
|
let handle: Box<dyn FlagCallback<U, Box<(dyn Any + 'static)>, Box<(dyn error::Error + 'static)>>> =
|
|
|
|
let handle: Box<dyn FlagCallback<U, Box<(dyn Any + 'static)>, eyre::Report>> =
|
|
|
|
Box::new(move |state, index, value| handle(state, index, value)
|
|
|
|
Box::new(move |state, index, value| handle(state, index, value)
|
|
|
|
.map(|x| -> Box<(dyn Any + 'static)> {Box::new(x)})
|
|
|
|
.map(|x| -> Box<(dyn Any + 'static)> {Box::new(x)})
|
|
|
|
.map_err(|x| -> Box<(dyn error::Error +'static)> {Box::new(x)}));
|
|
|
|
.map_err(Into::into));
|
|
|
|
self.flags.push((kind, desc.into(), value, handle, single, true));
|
|
|
|
self.flags.push((kind, desc.into(), value, handle, single, true));
|
|
|
|
self.flags.len()-1
|
|
|
|
self.flags.len()-1
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -208,7 +223,7 @@ impl<U> ArgState<U>
|
|
|
|
/// # Note
|
|
|
|
/// # Note
|
|
|
|
/// This mutates the argument state container indefinately, and multiple calls to it will keep the mutated state.
|
|
|
|
/// This mutates the argument state container indefinately, and multiple calls to it will keep the mutated state.
|
|
|
|
/// So this might not behave as expected (e.g. callbacks marked `single` that were fired in the first call will not be fired in the second, etc.)
|
|
|
|
/// So this might not behave as expected (e.g. callbacks marked `single` that were fired in the first call will not be fired in the second, etc.)
|
|
|
|
pub fn parse<I,T: Into<String>>(&mut self, input: I) -> Result<Vec<Box<dyn Any+'static>>, Error>
|
|
|
|
pub fn parse<I,T: Into<String>>(&mut self, input: I) -> Result<Vec<Box<dyn Any+'static>>, CtxError>
|
|
|
|
where I: Iterator<Item=T>
|
|
|
|
where I: Iterator<Item=T>
|
|
|
|
{
|
|
|
|
{
|
|
|
|
let mut output = Vec::with_capacity(self.flags.len());
|
|
|
|
let mut output = Vec::with_capacity(self.flags.len());
|
|
|
@ -245,19 +260,78 @@ impl<U> ArgState<U>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Error without context
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Error
|
|
|
|
pub struct Error
|
|
|
|
{
|
|
|
|
{
|
|
|
|
flag: Kind<'static>,
|
|
|
|
flag: Kind<'static>,
|
|
|
|
arg: (usize, String),
|
|
|
|
arg: (usize, String),
|
|
|
|
desc: Option<String>,
|
|
|
|
desc: Option<String>,
|
|
|
|
from: Box<dyn error::Error + 'static>,
|
|
|
|
from: eyre::Report,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// An argument parsing error with context
|
|
|
|
|
|
|
|
#[repr(transparent)]
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
|
|
|
pub struct CtxError(Error);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
|
|
|
|
pub struct ErrorContext<'a>
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pub flag: &'a Kind<'static>,
|
|
|
|
|
|
|
|
pub arg: &'a String,
|
|
|
|
|
|
|
|
pub arg_idx: &'a usize,
|
|
|
|
|
|
|
|
pub desc: Option<&'a String>,
|
|
|
|
|
|
|
|
pub from: &'a eyre::Report,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl CtxError
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
/// The inner error
|
|
|
|
|
|
|
|
#[inline] pub fn inner(&self) -> &Error
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
&self.0
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Strips the context and returns the inner error
|
|
|
|
|
|
|
|
#[inline] fn into_inner(self) -> Error
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
self.0
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The context of this error
|
|
|
|
|
|
|
|
pub fn context(&self) -> ErrorContext<'_>
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
ErrorContext {
|
|
|
|
|
|
|
|
flag: &self.0.flag,
|
|
|
|
|
|
|
|
arg: &self.0.arg.1,
|
|
|
|
|
|
|
|
arg_idx: &self.0.arg.0,
|
|
|
|
|
|
|
|
desc: self.0.desc.as_ref(),
|
|
|
|
|
|
|
|
from: &self.0.from
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Which argument was the error thrown for
|
|
|
|
|
|
|
|
pub fn what(&self) -> (usize, &str)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
(self.0.arg.0, &self.0.arg.1[..])
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Where did the parsing fail?
|
|
|
|
|
|
|
|
pub fn which(&self) -> Kind<'_>
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
self.0.flag.as_ref()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// The message reported by the failing callback, if any
|
|
|
|
|
|
|
|
pub fn message(&self) -> Option<&str>
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
self.0.desc.as_ref().map(|x| &x[..])
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl error::Error for Error
|
|
|
|
impl error::Error for Error
|
|
|
|
{
|
|
|
|
{
|
|
|
|
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
|
|
|
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
|
|
|
Some(self.from.as_ref())
|
|
|
|
self.from.source()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -265,12 +339,86 @@ impl fmt::Display for Error
|
|
|
|
{
|
|
|
|
{
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
|
|
{
|
|
|
|
{
|
|
|
|
writeln!(f, "Failed to parse args: {}", self.from)?;
|
|
|
|
writeln!(f, "Failed to parse args")
|
|
|
|
writeln!(f, "\tArg {} which was {:?} failed on parsing for `{:?}`", self.arg.0, self.arg.1, self.flag)?;
|
|
|
|
}
|
|
|
|
if let Some(desc) = &self.desc {
|
|
|
|
}
|
|
|
|
writeln!(f, "\n\tUsage of {:?}: {}", self.flag, desc)
|
|
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for CtxError
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
#[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
self.0.fmt(f)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Error
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
/// Consume into an `eyre::Report`.
|
|
|
|
|
|
|
|
fn into_report(self) -> eyre::Report
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
let Error {flag, desc, from ,arg: (idx, arg)} = self;
|
|
|
|
|
|
|
|
let err = Err::<!, _>(from)
|
|
|
|
|
|
|
|
//.wrap_err(eyre!("Failed to parse args"))
|
|
|
|
|
|
|
|
.with_section(move || format!("{:?}", flag).header("While attempting to parse for"))
|
|
|
|
|
|
|
|
.with_note(move || idx.to_string().header("Argument index was"))
|
|
|
|
|
|
|
|
.with_note(move || format!("{:?}", arg).header("Argument was"));
|
|
|
|
|
|
|
|
if let Some(desc) = desc {
|
|
|
|
|
|
|
|
err.with_suggestion(move || desc)
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
err
|
|
|
|
}
|
|
|
|
}.unwrap_err()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Add context to this error
|
|
|
|
|
|
|
|
pub fn with_context(self) -> CtxError
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
CtxError(Self {
|
|
|
|
|
|
|
|
flag: self.flag.clone(),
|
|
|
|
|
|
|
|
arg: self.arg.clone(),
|
|
|
|
|
|
|
|
desc: self.desc.clone(),
|
|
|
|
|
|
|
|
from: self.into_report(),
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl From<Error> for CtxError
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
fn from(from: Error) -> Self
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
from.with_context()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl From<CtxError> for eyre::Report
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
fn from(from: CtxError) -> Self
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
from.0.from
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
|
|
mod tests
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
|
|
fn errors() -> Result<(), eyre::Report>
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
//color_eyre::install()?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let err= Error {
|
|
|
|
|
|
|
|
flag: Kind::Long(&["test"], false),
|
|
|
|
|
|
|
|
arg: (0, "owo".to_owned()),
|
|
|
|
|
|
|
|
desc: Some("expected between 1-10".to_string()),
|
|
|
|
|
|
|
|
from: eyre!("Invalid number").wrap_err("Parsing failure"),
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
println!("No ctx: `{} {:?}`\n", err, err);
|
|
|
|
|
|
|
|
let err = err.with_context();
|
|
|
|
|
|
|
|
println!("With ctx: `{} {:?}`\n", err, err);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//return Err(err)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|