parent
1a88bc149d
commit
6a8d4ccd1e
@ -0,0 +1,286 @@
|
||||
//! For parsing arguments
|
||||
use super::*;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::mem::Discriminant;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Argument
|
||||
{
|
||||
ModeChangeHelp,
|
||||
LimitConcMaxProc,
|
||||
LimitConc(NonZeroUsize),
|
||||
UnlimitConc,
|
||||
|
||||
Save(String),
|
||||
SaveStdout,
|
||||
SaveRaw(String),
|
||||
SaveRawStdout,
|
||||
|
||||
LimitRecurse(NonZeroUsize),
|
||||
UnlimitRecurse,
|
||||
|
||||
LogVerbose,
|
||||
LogQuiet,
|
||||
LogSilent,
|
||||
|
||||
StopReading,
|
||||
|
||||
Input(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for Argument
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
use Argument::*;
|
||||
match self
|
||||
{
|
||||
ModeChangeHelp => write!(f, "--help"),
|
||||
LimitConcMaxProc => write!(f, "-m"),
|
||||
LimitConc(limit) => write!(f, "--threads {}", limit),
|
||||
UnlimitConc => write!(f, "-M"),
|
||||
|
||||
Save(s) => write!(f, "--save {:?}", s),
|
||||
SaveStdout => write!(f, "-D"),
|
||||
SaveRaw(s) => write!(f, "--save-raw {:?}", s),
|
||||
SaveRawStdout => write!(f, "-R"),
|
||||
|
||||
LimitRecurse(rec) => write!(f, "--recursive {}", rec),
|
||||
UnlimitRecurse => write!(f, "-r"),
|
||||
|
||||
LogVerbose => write!(f, "-v"),
|
||||
LogQuiet => write!(f, "-q"),
|
||||
LogSilent => write!(f, "-Q"),
|
||||
|
||||
StopReading => write!(f, "-"),
|
||||
|
||||
Input(input) => write!(f, "<{}>", input),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
|
||||
enum MX
|
||||
{
|
||||
None,
|
||||
Itself,
|
||||
All,
|
||||
Only(Discriminant<Argument>),
|
||||
Many(&'static [Discriminant<Argument>]),
|
||||
}
|
||||
|
||||
impl Default for MX
|
||||
{
|
||||
#[inline]
|
||||
fn default() -> Self
|
||||
{
|
||||
Self::Itself
|
||||
}
|
||||
}
|
||||
|
||||
impl MX
|
||||
{
|
||||
/// Is this argument discriminant mutually exclusive with this other argument?
|
||||
pub fn is_mx(&self, this: Discriminant<Argument>, other: &Argument) -> bool
|
||||
{
|
||||
use std::mem::discriminant;
|
||||
|
||||
let other = discriminant(other);
|
||||
match self
|
||||
{
|
||||
Self::Itself if other == this => true,
|
||||
Self::All => true,
|
||||
Self::Only(disc) if other == *disc => true,
|
||||
Self::Many(discs) if discs.contains(&other) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Argument
|
||||
{
|
||||
/// Is this `Argument` mutually exclusive with another?
|
||||
pub fn is_mx_with(&self, other: &Self) -> bool
|
||||
{
|
||||
use std::mem::discriminant;
|
||||
lazy_static! {
|
||||
static ref MX_REF: HashMap<Discriminant<Argument>, MaybeVec<MX>> = {
|
||||
let mut out = HashMap::new();
|
||||
macro_rules! mx {
|
||||
(@) => {
|
||||
std::iter::empty()
|
||||
};
|
||||
(@ self $($tt:tt)*) => {
|
||||
iter![MX::Itself].chain(mx!(@ $($tt)*))
|
||||
};
|
||||
|
||||
(@ [$inner:expr] $($tt:tt)*) => {
|
||||
iter![MX::Only(discriminant(&$inner))].chain(mx!(@ $($tt)*))
|
||||
};
|
||||
(@ [$($inner:expr),*] $($tt:tt)*) => {
|
||||
iter![MX::Many(vec![$(discriminant(&$inner)),*].leak())].chain(mx!(@ $($tt)*))
|
||||
};
|
||||
(@ $ident:ident $($tt:tt)*) => {
|
||||
iter![MX::$ident].chain(mx!(@ $($tt)*))
|
||||
};
|
||||
($disc:expr => $($tt:tt)*) => {
|
||||
out.insert(discriminant(&$disc), mx!(@ $($tt)*).collect());
|
||||
};
|
||||
}
|
||||
|
||||
mx!(Argument::ModeChangeHelp => All);
|
||||
|
||||
mx!(Argument::LimitConcMaxProc => self [Argument::UnlimitConc,
|
||||
Argument::LimitConc(unsafe{NonZeroUsize::new_unchecked(1)})]);
|
||||
mx!(Argument::UnlimitConc => self [Argument::LimitConcMaxProc, Argument::LimitConc(unsafe{NonZeroUsize::new_unchecked(1)})]);
|
||||
mx!(Argument::LimitConc(unsafe{NonZeroUsize::new_unchecked(1)}) => self [Argument::LimitConcMaxProc, Argument::UnlimitConc]);
|
||||
|
||||
mx!(Argument::Save(String::default()) => self [Argument::SaveStdout,
|
||||
Argument::SaveRaw(Default::default()),
|
||||
Argument::SaveRawStdout]);
|
||||
mx!(Argument::SaveStdout => self [Argument::Save(String::default()),
|
||||
Argument::SaveRaw(Default::default()),
|
||||
Argument::SaveRawStdout]);
|
||||
mx!(Argument::SaveRaw(Default::default()) => self [Argument::Save(String::default()),
|
||||
Argument::SaveStdout,
|
||||
Argument::SaveRawStdout]);
|
||||
mx!(Argument::SaveRawStdout => self [Argument::Save(String::default()),
|
||||
Argument::SaveRaw(String::default()),
|
||||
Argument::SaveStdout]);
|
||||
mx!(Argument::LimitRecurse(unsafe{NonZeroUsize::new_unchecked(1)}) => self [Argument::UnlimitRecurse]);
|
||||
mx!(Argument::UnlimitRecurse => self [Argument::LimitRecurse(unsafe{NonZeroUsize::new_unchecked(1)})]);
|
||||
mx!(Argument::LogVerbose => self [Argument::LogQuiet, Argument::LogSilent]);
|
||||
mx!(Argument::LogQuiet => self [Argument::LogVerbose, Argument::LogSilent]);
|
||||
mx!(Argument::LogSilent => self [Argument::LogQuiet, Argument::LogVerbose]);
|
||||
|
||||
mx!(Argument::StopReading => All);
|
||||
|
||||
mx!(Argument::Input(String::default()) => None);
|
||||
out
|
||||
};
|
||||
}
|
||||
let this = discriminant(self);
|
||||
match MX_REF.get(&this) {
|
||||
Some(mx) if mx.iter().filter(|mx| mx.is_mx(this, other)).next().is_some() => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Should we continue parsing and/or reading arguments?
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Copy)]
|
||||
pub enum Continue
|
||||
{
|
||||
/// Keep parsing the arguments
|
||||
Yes,
|
||||
/// Stop parsing arguments, add the rest of args as `Input`s
|
||||
No,
|
||||
/// On mode change, we don't need to parse the rest of the argument. Stop reading entirely.
|
||||
Abort,
|
||||
}
|
||||
|
||||
impl Continue
|
||||
{
|
||||
/// Should we keep *parsing* args?
|
||||
#[inline] pub fn keep_reading(&self) -> bool
|
||||
{
|
||||
if let Self::Yes = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Continue
|
||||
{
|
||||
#[inline]
|
||||
fn default() -> Self
|
||||
{
|
||||
Self::Yes
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Continue
|
||||
{
|
||||
fn from(from: bool) -> Self
|
||||
{
|
||||
if from {
|
||||
Self::Yes
|
||||
} else {
|
||||
Self::No
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub type Output = HashSet<Argument>;
|
||||
#[inline] const fn suggestion_intended_arg() -> &'static str {
|
||||
"If this was intended as a path instead of an option, use option `-` before it."
|
||||
}
|
||||
|
||||
fn parse_single<I>(args: &mut I, output: &mut Output, this: char) -> eyre::Result<Continue>
|
||||
where I: Iterator<Item=String>
|
||||
|
||||
{
|
||||
//TODO: Parse single letter args
|
||||
Ok(Continue::Yes)
|
||||
}
|
||||
|
||||
pub fn parse_next<I>(args: &mut I, output: &mut Output, this: String) -> eyre::Result<Continue>
|
||||
where I: Iterator<Item=String>
|
||||
{
|
||||
let mut keep_reading = Continue::Yes;
|
||||
let item = match this.trim()
|
||||
{
|
||||
"--threads" => {
|
||||
let max = args.next().ok_or(eyre!("`--threads` expects a parameter"))
|
||||
.with_suggestion(suggestion_intended_arg.clone())?;
|
||||
match NonZeroUsize::new(max.parse::<usize>()
|
||||
.wrap_err(eyre!("`--threads` expects a non-negative number"))
|
||||
.with_suggestion(suggestion_intended_arg.clone())
|
||||
.with_section(move || max.header("Parameter given was"))?)
|
||||
{
|
||||
Some(max) => Argument::LimitConc(max),
|
||||
None => Argument::UnlimitConc,
|
||||
}
|
||||
},
|
||||
"--help" => {
|
||||
keep_reading = Continue::Abort;
|
||||
Argument::ModeChangeHelp
|
||||
},
|
||||
"-" => {
|
||||
return Ok(Continue::No);
|
||||
},
|
||||
//TODO: move rest of long args from `mod` to here
|
||||
single if single.starts_with("-") => {
|
||||
for ch in single.chars().skip(1) {
|
||||
match parse_single(args, output, ch)? {
|
||||
x @ Continue::No |
|
||||
x @ Continue::Abort => keep_reading = x,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
return Ok(keep_reading);
|
||||
},
|
||||
_ => {
|
||||
keep_reading = Continue::No;
|
||||
|
||||
Argument::Input(this)
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(mx) = output.iter().filter(|arg| item.is_mx_with(arg)).next() {
|
||||
return Err(eyre!("Arguments are mutually exclusive"))
|
||||
.with_section(|| item.header("Trying to add"))
|
||||
.with_section(|| mx.to_string().header("Which is mutually exclusive with"));
|
||||
}
|
||||
|
||||
output.insert(item); //TODO: Warn when adding duplicate?
|
||||
|
||||
Ok(keep_reading)
|
||||
}
|
Loading…
Reference in new issue