add Continue::Abort to arg parsing

arg-parsing-better
Avril 4 years ago
parent 1a88bc149d
commit 6a8d4ccd1e
Signed by: flanchan
GPG Key ID: 284488987C31F630

14
Cargo.lock generated

@ -2,6 +2,12 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "ad-hoc-iter"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90a8dd76beceb5313687262230fcbaaf8d4e25c37541350cf0932e9adb8309c8"
[[package]] [[package]]
name = "addr2line" name = "addr2line"
version = "0.14.1" version = "0.14.1"
@ -118,6 +124,7 @@ dependencies = [
name = "dirstat" name = "dirstat"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ad-hoc-iter",
"async-compression", "async-compression",
"cfg-if 1.0.0", "cfg-if 1.0.0",
"color-eyre", "color-eyre",
@ -131,6 +138,7 @@ dependencies = [
"pin-project", "pin-project",
"serde", "serde",
"serde_cbor", "serde_cbor",
"smallvec",
"tokio", "tokio",
] ]
@ -609,6 +617,12 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "smallvec"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.3.19" version = "0.3.19"

@ -32,6 +32,7 @@ defer-drop = []
splash = [] splash = []
[dependencies] [dependencies]
ad-hoc-iter = "0.2.3"
async-compression = {version = "0.3", features=["tokio-02", "bzip2"], optional=true} async-compression = {version = "0.3", features=["tokio-02", "bzip2"], optional=true}
cfg-if = "1.0.0" cfg-if = "1.0.0"
color-eyre = {version = "0.5.10", default-features=false} color-eyre = {version = "0.5.10", default-features=false}
@ -45,4 +46,5 @@ once_cell = "1.5.2"
pin-project = "1.0.5" pin-project = "1.0.5"
serde = {version = "1.0.123", features=["derive"], optional=true} serde = {version = "1.0.123", features=["derive"], optional=true}
serde_cbor = {version = "0.11.1", optional=true} serde_cbor = {version = "0.11.1", optional=true}
smallvec = "1.6.1"
tokio = {version = "0.2", features=["full"]} tokio = {version = "0.2", features=["full"]}

@ -5,6 +5,8 @@ use std::fmt;
use config::Config; use config::Config;
mod parsing;
/// Executable name /// Executable name
pub fn program_name() -> &'static str pub fn program_name() -> &'static str
{ {

@ -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)
}

@ -18,6 +18,8 @@ pub mod prelude
pub use super::StreamGateExt as _; pub use super::StreamGateExt as _;
pub use super::StreamLagExt as _; pub use super::StreamLagExt as _;
pub use super::INodeExt as _; pub use super::INodeExt as _;
pub use super::MaybeVec;
} }
pub trait INodeExt pub trait INodeExt
@ -346,3 +348,5 @@ impl fmt::Display for SoftAssertionFailedError
} }
}; };
} }
pub type MaybeVec<T> = smallvec::SmallVec<[T; 1]>;

@ -4,6 +4,7 @@
#[macro_use] extern crate pin_project; #[macro_use] extern crate pin_project;
#[macro_use] extern crate lazy_static; #[macro_use] extern crate lazy_static;
#[macro_use] extern crate cfg_if; #[macro_use] extern crate cfg_if;
#[macro_use] extern crate ad_hoc_iter;
#[cfg(feature="inspect")] use serde::{Serialize, Deserialize}; #[cfg(feature="inspect")] use serde::{Serialize, Deserialize};

Loading…
Cancel
Save