display argument kind

args
Avril 4 years ago
parent 1a8792b4d0
commit bdbadf60b1
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -0,0 +1,151 @@
//! Errors for arg parsing
use super::*;
use flags::*;
use std::{
error,
fmt,
};
/// Error without context
#[derive(Debug)]
pub struct Error
{
pub(super) flag: Kind<'static>,
pub(super) arg: (usize, String),
pub(super) desc: Option<String>,
pub(super) from: eyre::Report,
}
/// An argument parsing error with context
#[repr(transparent)]
#[derive(Debug)]
pub struct ContextualError(Error);
#[derive(Debug, Clone, Copy)]
pub struct ErrorContext<'a>
{
/// The error-throwing flag
pub flag: &'a Kind<'static>,
/// The argument string
pub arg: &'a String,
/// The index of the argument string
pub arg_idx: &'a usize,
/// Message from the failing flag callback, if any
pub desc: Option<&'a String>,
/// The error report
pub from: &'a eyre::Report,
}
impl ContextualError
{
/// 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
{
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.from.source()
}
}
impl fmt::Display for Error
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
writeln!(f, "Failed to parse args")
}
}
impl fmt::Display for ContextualError
{
#[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
self.0.fmt(f)
}
}
impl Error
{
/// Consume into an `eyre::Report`.
pub(super) 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 || flag.to_string().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 {
err
}.unwrap_err()
}
/// Add context to this error
pub fn with_context(self) -> ContextualError
{
ContextualError(Self {
flag: self.flag.clone(),
arg: self.arg.clone(),
desc: self.desc.clone(),
from: self.into_report(),
})
}
}
impl From<Error> for ContextualError
{
#[inline] fn from(from: Error) -> Self
{
from.with_context()
}
}
impl From<ContextualError> for eyre::Report
{
#[inline] fn from(from: ContextualError) -> Self
{
from.0.from
}
}

@ -6,44 +6,144 @@ use std::{
error, error,
fmt, fmt,
}; };
use super::error::{
Error,
ContextualError,
};
use iterators::*;
type ShortFlag= (char,);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Kind<'a> pub enum Kind<'a>
{ {
/// Any number of these long-form arguments starting with `--`, and flag for case-sensitivity. /// 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). /// Can be empty to match just `--`
Long(&'a [&'a str], bool), Long(&'a [&'a str], bool),
/// Any number of these short-form (single character) arguments together or apart (starting with `-`). /// 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. /// Can be empty for a single `-` character, and a flag for case-sensitivity.
Short(&'a [(char, )], bool), Short(&'a [ShortFlag], bool),
/// A combination of any long + short flags
Abrev(&'a [&'a str], &'a [ShortFlag], bool, bool),
/// A single `--`. /// A single `--`.
///
/// # Note
/// Shorthand for `Long(&[], false)`
Terminator, Terminator,
/// An exact match with no prefix. /// A single `-`.
Exact(&'a str), ///
/// # Note
/// Shorthand for `Short(&[], false)`
TerminatorShort,
/// An exact match with no prefix and flag for case-sensitivity.
Exact(&'a str, bool),
/// Match everything,
Any,
} }
impl Kind<'static> impl<'b> Kind<'b>
{ {
pub fn as_ref<'a>(&'a self) -> Kind<'a> pub fn as_ref<'a>(&'a self) -> Kind<'a>
{ {
match &self { match &self {
Self::Abrev(strs, chrs, sl, cl) => Kind::Abrev(strs,chrs, *sl, *cl),
Self::Long(strs, bl) => Kind::Long(strs, *bl), Self::Long(strs, bl) => Kind::Long(strs, *bl),
Self::Short(chrs, bl) => Kind::Short(chrs, *bl), Self::Short(chrs, bl) => Kind::Short(chrs, *bl),
Self::Terminator => Kind::Terminator, Self::Terminator => Kind::Terminator,
Self::Exact(stri) => Kind::Exact(&stri[..]), Self::TerminatorShort => Kind::TerminatorShort,
Self::Exact(stri, bl) => Kind::Exact(&stri[..], *bl),
Self::Any => Kind::Any,
} }
} }
} }
/// Helper for `Cow` variants
macro_rules! cow
{
(& $ex:expr) => (Cow::Borrowed($ex.into()));
(~ $ex:expr) => (Cow::Owned($ex.into()));
}
impl<'a> fmt::Display for Kind<'a>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
//TODO: Change for this to write directly to `f` instead of allocating temporary strings and `Cow`ing to it.
write!(f, "{}", match self {
Self::Terminator |
Self::Long(&[], _) |
Self::Abrev(&[], &[], _, _) => cow!(& "--"),
Self::TerminatorShort |
Self::Short(&[], _) => cow!(& "-"),
Self::Exact(ex, case) => cow!(~ format!("`{}`{}", ex, if *case {""} else {"i"})),
Self::Any => cow!(& "..."),
Self::Long(slice, false) |
Self::Abrev(slice, &[], false, _)
=> cow!(~ slice.iter()
.map(|string| format!("--{}", string))
.join(", ")),
Self::Long(slice, true) |
Self::Abrev(slice, &[], true, _)
=> cow!(~ {
let mut string = slice.iter()
.map(|string| format!("--{}", string))
.join(", ");
string.push_str(" (case sensitive)");
string
}),
Self::Short(slice, true) |
Self::Abrev(&[], slice, _, true)
=> cow!(~ std::iter::once('-')
.chain(slice.iter().map(|x| x.0))
.collect::<String>()),
Self::Short(slice, false) |
Self::Abrev(&[], slice, _, false)
=> cow!(~ {
let mut string = std::iter::once('-')
.chain(slice.iter().map(|x| x.0))
.collect::<String>();
string.push_str(" (case insensitive)");
string
}),
Self::Abrev(long, short, lcase, scase) => return write!(f, "{}; {}", Self::Long(long, *lcase), Self::Short(short, *scase)),
})
}
}
/// What value(s) (if any) does this argument need
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum ValueKind pub enum ValueKind
{ {
/// No values, this is a boolean flag
None, None,
/// A value in the form of `--option=value`
Equals, Equals,
/// The next `n` arguments are the values
Next(usize), Next(usize),
/// The whole rest of the iterator is the values
Rest, Rest,
} }
impl Default for ValueKind
{
#[inline]
fn default() -> Self
{
Self::None
}
}
pub struct ArgState<T=()> pub struct ArgState<T=()>
{ {
user: T, user: T,
@ -72,69 +172,6 @@ impl<U> ArgState<U>
} }
} }
#[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. /// A handle to mutate the arg state.
@ -260,149 +297,6 @@ impl<U> ArgState<U>
} }
} }
/// Error without context
#[derive(Debug)]
pub struct Error
{
flag: Kind<'static>,
arg: (usize, String),
desc: Option<String>,
from: eyre::Report,
}
/// An argument parsing error with context
#[repr(transparent)]
#[derive(Debug)]
pub struct ContextualError(Error);
#[derive(Debug, Clone, Copy)]
pub struct ErrorContext<'a>
{
/// The error-throwing flag
pub flag: &'a Kind<'static>,
/// The argument string
pub arg: &'a String,
/// The index of the argument string
pub arg_idx: &'a usize,
/// Message from the failing flag callback, if any
pub desc: Option<&'a String>,
/// The error report
pub from: &'a eyre::Report,
}
impl ContextualError
{
/// 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
{
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.from.source()
}
}
impl fmt::Display for Error
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
writeln!(f, "Failed to parse args")
}
}
impl fmt::Display for ContextualError
{
#[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 {
err
}.unwrap_err()
}
/// Add context to this error
pub fn with_context(self) -> ContextualError
{
ContextualError(Self {
flag: self.flag.clone(),
arg: self.arg.clone(),
desc: self.desc.clone(),
from: self.into_report(),
})
}
}
impl From<Error> for ContextualError
{
#[inline] fn from(from: Error) -> Self
{
from.with_context()
}
}
impl From<ContextualError> for eyre::Report
{
#[inline] fn from(from: ContextualError) -> Self
{
from.0.from
}
}
#[cfg(test)] #[cfg(test)]
mod tests mod tests
@ -411,10 +305,10 @@ mod tests
#[test] #[test]
fn errors() -> Result<(), eyre::Report> fn errors() -> Result<(), eyre::Report>
{ {
//color_eyre::install()?; color_eyre::install()?;
let err= Error { let err= Error {
flag: Kind::Long(&["test"], false), flag: Kind::Abrev(&["test", "real-test", "more-tests"], &[('t',), ('R',)], false, true),
arg: (0, "owo".to_owned()), arg: (0, "owo".to_owned()),
desc: Some("expected between 1-10".to_string()), desc: Some("expected between 1-10".to_string()),
from: eyre!("Invalid number").wrap_err("Parsing failure"), from: eyre!("Invalid number").wrap_err("Parsing failure"),

@ -1,6 +1,9 @@
//! Parse args //! Parse args
use super::*; use super::*;
pub mod error;
pub mod flags;
pub fn usage() -> ! pub fn usage() -> !
{ {
println!(r#"Usage: {program} <files...> println!(r#"Usage: {program} <files...>

@ -0,0 +1,140 @@
//! Useful iterators
use super::*;
/// An iterator that can yield `0,1,2+` items
#[derive(Debug, Clone)]
pub 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 bespoke iterator type with an exact size
#[macro_export] macro_rules! over {
(@) => (0usize);
(@ $x:tt $($xs:tt)* ) => (1usize + $crate::over!(@ $($xs)*));
($($value:expr),*) => {
{
use ::std::mem::MaybeUninit;
use ::std::ops::Drop;
struct Arr<T>([MaybeUninit<T>; $crate::over!(@ $($value)*)], usize);
impl<T> Arr<T>
{
const LEN: usize = $crate::over!(@ $($value)*);
}
impl<T> Iterator for Arr<T>
{
type Item = T;
fn next(&mut self) -> Option<Self::Item>
{
if self.1 >= self.0.len() {
None
} else {
//take one
let one = unsafe {
::std::mem::replace(&mut self.0[self.1], MaybeUninit::uninit()).assume_init()
};
self.1+=1;
Some(one)
}
}
#[inline] fn size_hint(&self) -> (usize, Option<usize>)
{
(Self::LEN, Some(Self::LEN))
}
}
impl<T> ::std::iter::FusedIterator for Arr<T>{}
impl<T> ::std::iter::ExactSizeIterator for Arr<T>{}
impl<T> Drop for Arr<T>
{
fn drop(&mut self) {
if ::std::mem::needs_drop::<T>() {
for idx in self.1..self.0.len() {
unsafe {
::std::mem::replace(&mut self.0[idx], MaybeUninit::uninit()).assume_init();
}
}
}
}
}
Arr([$(MaybeUninit::new($value)),*], 0)
}
}
}
#[cfg(test)]
mod tests
{
#[test]
fn iter_over()
{
const EXPECT: usize = 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1;
let iter = over![10,9,8,7,6,5,4,3,2,1];
assert_eq!(iter.len(), 10);
assert_eq!(iter.sum::<usize>(), EXPECT);
}
}

@ -30,11 +30,11 @@ mod ext;
use ext::*; use ext::*;
mod util; mod util;
mod dedup; mod dedup;
mod iterators;
mod resolve; mod resolve;
mod database; mod database;
mod flags;
mod args; mod args;
mod config; mod config;

@ -135,3 +135,8 @@ pub fn defer<F: FnOnce()>(fun: F) -> impl std::ops::Drop
} }
DropWrap(Some(fun)) DropWrap(Some(fun))
} }
/// Simplification for single expression `if x {y} else {z}`
#[macro_export] macro_rules! tern {
($if:expr; $then:expr, $else:expr) => (if $if {$then} else {$else});
}

Loading…
Cancel
Save