start arg parsing scheme

master
Avril 4 years ago
parent 72bfc293cd
commit 1f96af62d6
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -0,0 +1,276 @@
//! CLI flags module
use super::*;
use std::{
borrow::Cow,
any::Any,
error,
fmt,
};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Kind<'a>
{
/// Any number of these long-form arguments starting with `--`, and flag for case-sensitivity.
/// Can be empty to match any argument (not needing a prefix).
Long(&'a [&'a str], bool),
/// Any number of these short-form (single character) arguments together or apart (starting with `-`).
/// Can be empty for a single `-` character, and a flag for case-sensitivity.
Short(&'a [(char, )], bool),
/// A single `--`.
Terminator,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ValueKind
{
None,
Equals,
Next(usize),
Rest,
}
pub struct ArgState<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)>,
}
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
where F: FnMut(&mut StateHandle<'_, U>, usize, &str) -> Result<T, E>,
T: Any + 'static,
E: 'static{}
impl<U> ArgState<U>
{
/// 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
where F: FlagCallback<U, T,E> + 'static
{
let handle: Box<dyn FlagCallback<U, Box<(dyn Any + 'static)>, Box<(dyn error::Error + 'static)>>> =
Box::new(move |state, index, value| handle(state, index, value)
.map(|x| -> Box<(dyn Any + 'static)> {Box::new(x)})
.map_err(|x| -> Box<(dyn error::Error +'static)> {Box::new(x)}));
self.flags.push((kind, desc.into(), value, handle, single, true));
self.flags.len()-1
}
}
#[derive(Debug, Clone)]
enum Either<T,U>
{
None,
One(T),
Many(U),
}
impl<T,U> Either<T,U>
{
pub fn take(&mut self) -> Self
{
std::mem::replace(self, Self::None)
}
}
#[derive(Debug, Clone)]
pub enum EitherIter<T,U>
{
None,
One(std::iter::Once<T>),
Many(std::iter::Fuse<U>),
}
impl<T,U> Iterator for EitherIter<T,U>
where U: Iterator<Item=T>
{
type Item = T;
fn next(&mut self) -> Option<Self::Item>
{
match self {
Self::None => None,
Self::One(one) => one.next(),
Self::Many(many) => many.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
Self::None => (0, Some(0)),
Self::One(_) => (1, Some(1)),
Self::Many(many) => many.size_hint(),
}
}
}
impl<T,U: Iterator<Item=T>> std::iter::FusedIterator for EitherIter<T,U>{}
impl<T,U: Iterator<Item=T>> std::iter::ExactSizeIterator for EitherIter<T,U>
where U: ExactSizeIterator{}
impl<T, U: IntoIterator<Item=T>> IntoIterator for Either<T, U>
{
type Item= T;
type IntoIter = EitherIter<T, <U as IntoIterator>::IntoIter>;
fn into_iter(self) -> Self::IntoIter
{
match self {
Self::None => EitherIter::None,
Self::One(one) => EitherIter::One(std::iter::once(one)),
Self::Many(many) => EitherIter::Many(many.into_iter().fuse())
}
}
}
/// A handle to mutate the arg state.
pub struct StateHandle<'a, T>
{
held: Either<String, Vec<String>>,
state: &'a mut T,
args: &'a mut (dyn Iterator<Item=String> + 'a),
chk: &'a mut bool,
idx: usize,
}
impl<'a, T> StateHandle<'a,T>
{
/// The user defined state
pub fn state(&self) -> &T
{
self.state
}
/// The mutable user defined state
pub fn state_mut(&mut self) -> &mut T
{
self.state
}
/// The current args iterator
pub fn args(&self) -> &(dyn Iterator<Item=String> + 'a)
{
self.args
}
/// The mutable current args iterator
pub fn args_mut(&mut self) -> &mut (dyn Iterator<Item = String> + 'a)
{
self.args
}
/// Is this callback enabled?
///
/// It will always start as `true`. This can be mutated to `false` to disable further checks for this argument.
pub fn enabled_mut(&mut self) -> &mut bool
{
self.chk
}
/// Is this callback enabled?
///
/// This should always be `true`.
pub fn enabled(&mut self) -> bool
{
*self.chk
}
/// The index of the current arg
pub fn index(&self) -> usize
{
self.idx
}
/// The held values taken from the argument iterator as specified by the argument's definition.
///
/// # Note
/// This moves the values, if you want to call this more than once, use `clone_held`.
/// If called more than once will yield no values
pub fn extract_held(&mut self) -> EitherIter<String, std::vec::IntoIter<String>>
{
self.held.take().into_iter()
}
/// Clone the held values taken from the argument iterator for this invokation.
///
/// # Note
/// This clones all the values, instead of moving like `extract_held`, so multiple calls to this will yield the same clones.
/// However, if `extract_held` has been called, this will yield no values.
pub fn clone_held(&self) -> EitherIter<String, std::vec::IntoIter<String>>
{
self.held.clone().into_iter()
}
}
impl<U> ArgState<U>
{
/// Parse this argument iterator.
///
/// # Note
/// 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.)
pub fn parse<I,T: Into<String>>(&mut self, input: I) -> Result<Vec<Box<dyn Any+'static>>, Error>
where I: Iterator<Item=T>
{
let mut output = Vec::with_capacity(self.flags.len());
let mut input = input.map(Into::into).fuse();
let mut i=0;
while let Some(arg) = input.next()
{
for (kind, desc, value, callback, single, chk) in self.flags.iter_mut().filter(|(_,_,_,_,_,chk)| *chk)
{
if *single {
*chk = false;
}
//TODO: Check Kind matches `arg`, check `value` matches `input` and get the values.
output.push(callback(&mut StateHandle{
state: &mut self.user,
args: &mut input,
chk,
idx: i,
held: Either::None, //TODO: This will be for either one `=<value>` or many `<values...>` from args as according to `value`.
}, i, &arg[..])
.map_err(|err| Error{flag: kind.clone(), from: err, desc: Some(desc.clone().into_owned()), arg: (i, arg.clone())})?);
}
i+=1;
}
Ok(output)
}
/// Consume into the user-provided state value
#[inline] pub fn into_inner(self) -> U
{
self.user
}
}
#[derive(Debug)]
pub struct Error
{
flag: Kind<'static>,
arg: (usize, String),
desc: Option<String>,
from: Box<dyn error::Error + 'static>,
}
impl error::Error for Error
{
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(self.from.as_ref())
}
}
impl fmt::Display for Error
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
writeln!(f, "Failed to parse args: {}", self.from)?;
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)
} else {
Ok(())
}
}
}

@ -34,6 +34,7 @@ mod dedup;
mod resolve;
mod database;
mod flags;
mod args;
mod config;

Loading…
Cancel
Save