Simplify + Cli
This commit is contained in:
parent
9d17672a76
commit
928d4ce0ba
166
Cargo.lock
generated
166
Cargo.lock
generated
|
|
@ -5,169 +5,3 @@ version = 3
|
|||
[[package]]
|
||||
name = "e"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"e-easy-default",
|
||||
"e-easy-from",
|
||||
"e-easy-get",
|
||||
"e-impl-for-refs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "e-easy-default"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"e-easy-default-core",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "e-easy-default-core"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "e-easy-from"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"e-easy-from-core",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "e-easy-from-core"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "e-easy-get"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"e-easy-get-core",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "e-easy-get-core"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"e-easy-default",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "e-impl-for-refs"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"e-impl-for-refs-core",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "e-impl-for-refs-core"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
|
|
|||
33
Cargo.toml
33
Cargo.toml
|
|
@ -3,27 +3,18 @@ name = "e"
|
|||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"proc-macros/easy-default/core",
|
||||
"proc-macros/easy-default",
|
||||
"proc-macros/easy-from/core",
|
||||
"proc-macros/easy-from",
|
||||
"proc-macros/easy-get/core",
|
||||
"proc-macros/easy-get",
|
||||
"proc-macros/impl-for-refs/core",
|
||||
"proc-macros/impl-for-refs",
|
||||
]
|
||||
resolver = "2"
|
||||
# [workspace]
|
||||
# members = []
|
||||
# resolver = "2"
|
||||
|
||||
[dependencies]
|
||||
e-easy-default = { path = "proc-macros/easy-default" }
|
||||
e-easy-from = { path = "proc-macros/easy-from" }
|
||||
e-easy-get = { path = "proc-macros/easy-get" }
|
||||
e-impl-for-refs = { path = "proc-macros/impl-for-refs" }
|
||||
# e-easy-default = { path = "proc-macros/easy-default" }
|
||||
# e-easy-from = { path = "proc-macros/easy-from" }
|
||||
# e-easy-get = { path = "proc-macros/easy-get" }
|
||||
# e-impl-for-refs = { path = "proc-macros/impl-for-refs" }
|
||||
|
||||
[workspace.dependencies]
|
||||
proc-macro-error = "1.0"
|
||||
proc-macro2 = "1"
|
||||
syn = { version = "2", features = ["full"] }
|
||||
quote = "1"
|
||||
# [workspace.dependencies]
|
||||
# proc-macro-error = "1.0"
|
||||
# proc-macro2 = "1"
|
||||
# syn = { version = "2", features = ["full"] }
|
||||
# quote = "1"
|
||||
|
|
|
|||
556
src/lib.rs
556
src/lib.rs
|
|
@ -1,224 +1,3 @@
|
|||
/*
|
||||
#[cfg(derive)]
|
||||
pub mod derive {
|
||||
pub use e_easy_default::EasyDefault;
|
||||
pub use e_easy_from::EasyFrom;
|
||||
pub use e_easy_get::EasyGet;
|
||||
}
|
||||
|
||||
#[cfg(meta)]
|
||||
pub mod meta {
|
||||
pub use e_impl_for_refs::impl_for_refs;
|
||||
}
|
||||
|
||||
pub mod args {
|
||||
use std::error::Error;
|
||||
|
||||
use e_easy_get::EasyGet;
|
||||
|
||||
#[derive(Debug, EasyGet, Eq, PartialEq)]
|
||||
pub struct MissingParamFor<T = &'static str> {
|
||||
#[get_ref(pub)]
|
||||
arg_name: T,
|
||||
}
|
||||
impl<T: core::fmt::Debug> core::fmt::Display for MissingParamFor<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
impl<T: core::fmt::Debug> Error for MissingParamFor<T> {
|
||||
}
|
||||
|
||||
pub enum Arg<S> {
|
||||
/// An arg that starts with a single `-` character. If multiple
|
||||
/// characters follow the `-`, they will be returned as multiple,
|
||||
/// separate Shorts
|
||||
Short(char),
|
||||
/// An arg that starts with `--`
|
||||
Long(S),
|
||||
/// An arg that has no preceding dashes
|
||||
Param(S),
|
||||
}
|
||||
|
||||
enum ArgParserState {
|
||||
ParsingSingle(std::vec::IntoIter<char>),
|
||||
UseIterator,
|
||||
}
|
||||
|
||||
pub struct ArgParser<I, S> {
|
||||
iterator: I,
|
||||
state: ArgParserState,
|
||||
stored: Option<S>,
|
||||
}
|
||||
|
||||
impl<I, S> ArgParser<I, S>
|
||||
where
|
||||
I: Iterator<Item = S>,
|
||||
S: AsRef<str>,
|
||||
{
|
||||
pub fn from_iterator(iterator: I) -> Self {
|
||||
Self {
|
||||
iterator,
|
||||
state: ArgParserState::UseIterator,
|
||||
stored: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn next(&mut self) -> Option<Arg<&str>> {
|
||||
loop {
|
||||
break match &mut self.state {
|
||||
ArgParserState::ParsingSingle(it) => {
|
||||
if let Some(c) = it.next() {
|
||||
Some(Arg::Short(c))
|
||||
} else {
|
||||
self.state = ArgParserState::UseIterator;
|
||||
continue;
|
||||
}
|
||||
},
|
||||
ArgParserState::UseIterator => {
|
||||
self.stored = Some(self.iterator.next()?);
|
||||
|
||||
let stored = self.stored.as_ref().unwrap();
|
||||
let next = stored.as_ref();
|
||||
if let Some(long) = next.strip_prefix("--") {
|
||||
Some(Arg::Long(long))
|
||||
} else if let Some(short) = next.strip_prefix('-') {
|
||||
let mut chars = short.chars();
|
||||
let out = chars.next()?;
|
||||
let chars = chars.collect::<Vec<_>>().into_iter();
|
||||
self.state = ArgParserState::ParsingSingle(chars);
|
||||
|
||||
Some(Arg::Short(out))
|
||||
} else {
|
||||
Some(Arg::Param(next))
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_param_for<T>(
|
||||
&mut self,
|
||||
arg_name: T,
|
||||
) -> Result<&str, MissingParamFor<T>> {
|
||||
match self.next() {
|
||||
Some(Arg::Param(p)) => Ok(p),
|
||||
_ => Err(MissingParamFor { arg_name }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArgParser<std::env::Args, String> {
|
||||
pub fn from_env() -> Self {
|
||||
let it = std::env::args();
|
||||
|
||||
Self::from_iterator(it)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::num::ParseIntError;
|
||||
|
||||
use e_easy_default::EasyDefault;
|
||||
use e_easy_from::EasyFrom;
|
||||
|
||||
use crate::args::{Arg, ArgParser, MissingParamFor};
|
||||
|
||||
#[test]
|
||||
fn test_arg_parser() {
|
||||
#[derive(EasyDefault, Debug, PartialEq, Eq)]
|
||||
struct Args {
|
||||
#[default("Hello world".to_string())]
|
||||
string: String,
|
||||
#[default(false)]
|
||||
bool: bool,
|
||||
#[default(8)]
|
||||
u64: u64,
|
||||
}
|
||||
|
||||
#[derive(EasyFrom, Debug, PartialEq, Eq)]
|
||||
#[from(infallible)]
|
||||
enum Error {
|
||||
#[from]
|
||||
MissingParamFor(MissingParamFor<&'static str>),
|
||||
#[from]
|
||||
IntParseError(ParseIntError),
|
||||
}
|
||||
|
||||
fn parse_args(strs: &[&str]) -> Result<Args, Error> {
|
||||
let mut parser = ArgParser::from_iterator(strs.iter());
|
||||
let mut args = Args::default();
|
||||
|
||||
while let Some(arg) = parser.next() {
|
||||
match arg {
|
||||
Arg::Long("string") => {
|
||||
args.string =
|
||||
parser.next_param_for("string")?.to_string()
|
||||
},
|
||||
Arg::Long("bool") => {
|
||||
args.bool = true;
|
||||
},
|
||||
Arg::Long("u64") => {
|
||||
args.u64 = parser.next_param_for("u64")?.parse()?
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
parse_args(&["--string"]),
|
||||
Err(Error::MissingParamFor(MissingParamFor {
|
||||
arg_name: "string"
|
||||
}))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod str {
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(
|
||||
Default, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug,
|
||||
)]
|
||||
pub struct NonEmptyStr<'a>(&'a str);
|
||||
impl<'a> AsRef<str> for NonEmptyStr<'a> {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
impl<'a> Display for NonEmptyStr<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait StrExt {
|
||||
fn non_empty(&self) -> Option<NonEmptyStr>;
|
||||
}
|
||||
|
||||
impl<T> StrExt for T
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn non_empty(&self) -> Option<NonEmptyStr> {
|
||||
let s = self.as_ref();
|
||||
|
||||
if s.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(NonEmptyStr(s))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
pub fn default<T: Default>() -> T {
|
||||
|
|
@ -242,3 +21,338 @@ where
|
|||
self.0(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod cli {
|
||||
//! Simple utilities for parsing arguments and generating command line help
|
||||
|
||||
use crate::default;
|
||||
use core::str::FromStr;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
InvalidParamFormat {
|
||||
param: &'static str,
|
||||
expected: &'static str,
|
||||
},
|
||||
MissingParamFor(&'static str),
|
||||
UnknownParam(String),
|
||||
SingleDashMustBeSingleCharacter(String),
|
||||
}
|
||||
|
||||
pub trait Value {
|
||||
fn assign(&mut self, param: &str) -> Result<(), &'static str>;
|
||||
}
|
||||
|
||||
trait FromStrValue: FromStr {
|
||||
fn help() -> &'static str;
|
||||
}
|
||||
macro_rules! impl_from_str_value {
|
||||
($t:ty) => {
|
||||
impl FromStrValue for $t {
|
||||
fn help() -> &'static str {
|
||||
stringify!($t)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
impl_from_str_value!(String);
|
||||
impl_from_str_value!(std::path::PathBuf);
|
||||
impl_from_str_value!(u8);
|
||||
impl_from_str_value!(u16);
|
||||
impl_from_str_value!(u32);
|
||||
impl_from_str_value!(u64);
|
||||
impl_from_str_value!(i8);
|
||||
impl_from_str_value!(i16);
|
||||
impl_from_str_value!(i32);
|
||||
impl_from_str_value!(i64);
|
||||
|
||||
impl<T> Value for T
|
||||
where
|
||||
T: FromStrValue,
|
||||
{
|
||||
fn assign(&mut self, param: &str) -> Result<(), &'static str> {
|
||||
*self = param.parse().map_err(|_| Self::help())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct-of-array-style storage
|
||||
struct Soa<'a, T: ?Sized> {
|
||||
ts: Vec<&'a mut T>,
|
||||
longs: Vec<&'static str>,
|
||||
descriptions: Vec<Option<&'static str>>,
|
||||
|
||||
// Shorts are not not 1:1 in length with
|
||||
// bindings, so we create a second linked vec that maps indices in
|
||||
// shorts to indices in bindings
|
||||
shorts: Vec<char>,
|
||||
shorts_indices: Vec<usize>,
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> Soa<'a, T> {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
ts: Vec::new(),
|
||||
longs: default(),
|
||||
descriptions: default(),
|
||||
shorts: default(),
|
||||
shorts_indices: default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn add(
|
||||
&mut self,
|
||||
t: &'a mut T,
|
||||
long: &'static str,
|
||||
short: Option<char>,
|
||||
description: Option<&'static str>,
|
||||
) {
|
||||
let next = self.ts.len();
|
||||
|
||||
self.ts.push(t);
|
||||
self.longs.push(long);
|
||||
self.descriptions.push(description);
|
||||
|
||||
if let Some(c) = short {
|
||||
self.shorts.push(c);
|
||||
self.shorts_indices.push(next);
|
||||
}
|
||||
}
|
||||
|
||||
fn find_for_long(
|
||||
&mut self,
|
||||
long: &str,
|
||||
) -> Option<(&'static str, &mut &'a mut T)> {
|
||||
self.longs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, s)| *s == &long)
|
||||
.map(|(i, s)| (*s, &mut self.ts[i]))
|
||||
}
|
||||
|
||||
fn find_for_short(
|
||||
&mut self,
|
||||
short: char,
|
||||
) -> Option<(&'static str, &mut &'a mut T)> {
|
||||
self.shorts
|
||||
.iter()
|
||||
.zip(self.shorts_indices.iter().copied())
|
||||
.find(|(s, _)| **s == short)
|
||||
.map(|(_, i)| (self.longs[i], &mut self.ts[i]))
|
||||
}
|
||||
}
|
||||
|
||||
type Params<'a> = Soa<'a, dyn Value + 'a>;
|
||||
type Flags<'a> = Soa<'a, bool>;
|
||||
|
||||
pub struct Cli<'a> {
|
||||
name: &'static str,
|
||||
description: Option<&'static str>,
|
||||
version: Option<&'static str>,
|
||||
|
||||
params: Params<'a>,
|
||||
flags: Flags<'a>,
|
||||
|
||||
errors: Vec<Error>,
|
||||
}
|
||||
impl<'a> Cli<'a> {
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
Self {
|
||||
name,
|
||||
description: default(),
|
||||
version: default(),
|
||||
|
||||
params: Soa::new(),
|
||||
flags: Soa::new(),
|
||||
|
||||
errors: default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn description(&mut self, d: &'static str) -> &mut Self {
|
||||
self.description = Some(d);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn version(&mut self, v: &'static str) -> &mut Self {
|
||||
self.version = Some(v);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn bind(&mut self, pb: ParamBuilder<'a>) {
|
||||
match pb.kind {
|
||||
Kind::Param(p) => {
|
||||
self.params.add(p, pb.long, pb.short, pb.description)
|
||||
},
|
||||
Kind::Flag(f) => {
|
||||
self.flags.add(f, pb.long, pb.short, pb.description)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse<'b, A: AsRef<str>, B: From<&'b str>>(
|
||||
&mut self,
|
||||
args: &'b [A],
|
||||
mut out: Option<&mut Vec<B>>,
|
||||
) {
|
||||
let mut it = args.iter();
|
||||
fn next_param<'a, A: AsRef<str> + 'a>(
|
||||
it: &mut impl Iterator<Item = &'a A>,
|
||||
name: &'static str,
|
||||
) -> Result<&'a str, Error> {
|
||||
it.next()
|
||||
.map(|s| s.as_ref())
|
||||
.ok_or(Error::MissingParamFor(name))
|
||||
}
|
||||
|
||||
while let Some(arg) = it.next() {
|
||||
let mut go = || {
|
||||
if let Some(arg) = arg.as_ref().strip_prefix("--") {
|
||||
if let Some((n, b)) = self.params.find_for_long(arg) {
|
||||
return b.assign(next_param(&mut it, n)?).map_err(
|
||||
|e| Error::InvalidParamFormat {
|
||||
param: n,
|
||||
expected: e,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
match self.flags.find_for_long(arg) {
|
||||
Some((_, b)) => {
|
||||
**b = true;
|
||||
Ok(())
|
||||
},
|
||||
None => Err(Error::UnknownParam(arg.to_string())),
|
||||
}
|
||||
} else if let Some(arg) = arg.as_ref().strip_prefix('-') {
|
||||
if arg.len() > 1 {
|
||||
return Err(
|
||||
Error::SingleDashMustBeSingleCharacter(
|
||||
arg.to_string(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let arg = arg.chars().next().unwrap();
|
||||
|
||||
if let Some((n, b)) = self.params.find_for_short(arg) {
|
||||
return b.assign(next_param(&mut it, n)?).map_err(
|
||||
|e| Error::InvalidParamFormat {
|
||||
param: n,
|
||||
expected: e,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
match self.flags.find_for_short(arg) {
|
||||
Some((_, b)) => {
|
||||
**b = true;
|
||||
Ok(())
|
||||
},
|
||||
None => Err(Error::UnknownParam(arg.to_string())),
|
||||
}
|
||||
} else {
|
||||
if let Some(out) = out.as_mut() {
|
||||
out.push(arg.as_ref().into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(e) = go().err() {
|
||||
self.errors.push(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_env(&mut self, out: Option<&mut Vec<String>>) {
|
||||
let env_args = std::env::args().collect::<Vec<_>>();
|
||||
self.parse(env_args.as_slice(), out);
|
||||
}
|
||||
}
|
||||
|
||||
enum Kind<'a> {
|
||||
Param(&'a mut dyn Value),
|
||||
Flag(&'a mut bool),
|
||||
}
|
||||
pub struct ParamBuilder<'a> {
|
||||
kind: Kind<'a>,
|
||||
long: &'static str,
|
||||
description: Option<&'static str>,
|
||||
short: Option<char>,
|
||||
}
|
||||
|
||||
pub fn param<'a>(
|
||||
long: &'static str,
|
||||
v: &'a mut dyn Value,
|
||||
) -> ParamBuilder<'a> {
|
||||
ParamBuilder {
|
||||
kind: Kind::Param(v),
|
||||
long,
|
||||
description: default(),
|
||||
short: default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flag<'a>(long: &'static str, v: &'a mut bool) -> ParamBuilder<'a> {
|
||||
ParamBuilder {
|
||||
kind: Kind::Flag(v),
|
||||
long,
|
||||
description: default(),
|
||||
short: default(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ParamBuilder<'a> {
|
||||
pub fn short(mut self, s: char) -> Self {
|
||||
self.short = Some(s);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn description(mut self, d: &'static str) -> Self {
|
||||
self.description = Some(d);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn bind(self, cli: &mut Cli<'a>) {
|
||||
cli.bind(self);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let mut cli = Cli::new("test");
|
||||
cli.description("A simple test CLI");
|
||||
cli.version("0.0.0");
|
||||
|
||||
let mut foo = String::new();
|
||||
param("foo", &mut foo)
|
||||
.short('f')
|
||||
.description("foo")
|
||||
.bind(&mut cli);
|
||||
|
||||
let mut bar: u64 = 0;
|
||||
param("bar", &mut bar).description("bar").bind(&mut cli);
|
||||
|
||||
let mut baz: bool = false;
|
||||
flag("baz", &mut baz)
|
||||
.short('b')
|
||||
.description("baz")
|
||||
.bind(&mut cli);
|
||||
|
||||
let mut args: Vec<&str> = Vec::new();
|
||||
cli.parse(
|
||||
&["--foo", "foo", "--bar", "854", "-b", "arg"],
|
||||
Some(&mut args),
|
||||
);
|
||||
assert_eq!(foo, "foo");
|
||||
assert_eq!(bar, 854);
|
||||
assert!(baz);
|
||||
assert_eq!(args, &["arg"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue