mirror of
https://github.com/uutils/uutils-args.git
synced 2026-06-10 16:13:08 -07:00
trailing var args via last keyword
This commit is contained in:
+24
-13
@@ -24,6 +24,7 @@ pub(crate) enum ArgType {
|
||||
},
|
||||
Positional {
|
||||
num_args: RangeInclusive<usize>,
|
||||
last: bool,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -64,6 +65,7 @@ pub(crate) fn parse_argument(v: Variant) -> Option<Argument> {
|
||||
assert!(field.is_some(), "Positional arguments must have a field");
|
||||
ArgType::Positional {
|
||||
num_args: pos.num_args,
|
||||
last: pos.last,
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -237,8 +239,8 @@ pub(crate) fn positional_handling(args: &[Argument]) -> (TokenStream, TokenStrea
|
||||
let mut missing_argument_checks = vec![];
|
||||
|
||||
for arg @ Argument { name, arg_type, .. } in args {
|
||||
let num_args = match arg_type {
|
||||
ArgType::Positional { num_args } => num_args,
|
||||
let (num_args, last) = match arg_type {
|
||||
ArgType::Positional { num_args, last } => (num_args, last),
|
||||
ArgType::Option { .. } => continue,
|
||||
};
|
||||
|
||||
@@ -251,7 +253,11 @@ pub(crate) fn positional_handling(args: &[Argument]) -> (TokenStream, TokenStrea
|
||||
|
||||
last_index += num_args.end();
|
||||
|
||||
let expr = argument_expression(arg);
|
||||
let expr = if *last {
|
||||
last_positional_expression(&arg.ident)
|
||||
} else {
|
||||
positional_expression(&arg.ident)
|
||||
};
|
||||
match_arms.push(quote!(0..=#last_index => { #expr }));
|
||||
}
|
||||
|
||||
@@ -303,14 +309,19 @@ fn required_value_expression(ident: &Ident) -> TokenStream {
|
||||
quote!(Self::#ident(FromValue::from_value(parser.value()?)?))
|
||||
}
|
||||
|
||||
fn argument_expression(arg: &Argument) -> TokenStream {
|
||||
let Argument {
|
||||
ident, arg_type, ..
|
||||
} = arg;
|
||||
match arg_type {
|
||||
ArgType::Positional { .. } => quote!(
|
||||
Self::#ident(FromValue::from_value(value)?)
|
||||
),
|
||||
_ => panic!("WWWOWOWOWOW"),
|
||||
}
|
||||
fn positional_expression(ident: &Ident) -> TokenStream {
|
||||
quote!(
|
||||
Self::#ident(FromValue::from_value(value)?)
|
||||
)
|
||||
}
|
||||
|
||||
fn last_positional_expression(ident: &Ident) -> TokenStream {
|
||||
quote!({
|
||||
let raw_args = parser.raw_args()?;
|
||||
let collection = std::iter::once(value)
|
||||
.chain(raw_args)
|
||||
.map(FromValue::from_value)
|
||||
.collect::<Result<_,_>>()?;
|
||||
Self::#ident(collection)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ enum AttributeArguments {
|
||||
Value(Expr),
|
||||
NumArgs(RangeInclusive<usize>),
|
||||
File(String),
|
||||
Last,
|
||||
}
|
||||
|
||||
impl AttributeArguments {
|
||||
@@ -93,11 +94,15 @@ impl ValueAttr {
|
||||
|
||||
pub(crate) struct PositionalAttr {
|
||||
pub(crate) num_args: RangeInclusive<usize>,
|
||||
pub(crate) last: bool,
|
||||
}
|
||||
|
||||
impl Default for PositionalAttr {
|
||||
fn default() -> Self {
|
||||
Self { num_args: 1..=1 }
|
||||
Self {
|
||||
num_args: 1..=1,
|
||||
last: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +113,7 @@ impl PositionalAttr {
|
||||
for arg in AttributeArguments::parse_all(attr) {
|
||||
match arg {
|
||||
AttributeArguments::NumArgs(k) => positional_attr.num_args = k,
|
||||
AttributeArguments::Last => positional_attr.last = true,
|
||||
_ => panic!(),
|
||||
};
|
||||
}
|
||||
@@ -207,7 +213,16 @@ impl Parse for AttributeArguments {
|
||||
|
||||
if input.peek(Ident) {
|
||||
let name = input.parse::<Ident>()?.to_string();
|
||||
|
||||
// Arguments that do not take values
|
||||
match name.as_str() {
|
||||
"last" => return Ok(Self::Last),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
input.parse::<Token![=]>()?;
|
||||
|
||||
// Arguments that do take values
|
||||
match name.as_str() {
|
||||
"parser" => return Ok(Self::Parser(input.parse::<Expr>()?)),
|
||||
"default" => return Ok(Self::Default(input.parse::<Expr>()?)),
|
||||
|
||||
+48
-7
@@ -1,8 +1,5 @@
|
||||
use std::ffi::OsString;
|
||||
use uutils_args::{Arguments, Options};
|
||||
|
||||
const EMPTY: [OsString; 0] = [];
|
||||
|
||||
#[test]
|
||||
fn one_positional() {
|
||||
#[derive(Arguments, Clone)]
|
||||
@@ -21,7 +18,7 @@ fn one_positional() {
|
||||
let settings = Settings::parse(["test", "foo"]).unwrap();
|
||||
assert_eq!(settings.file1, "foo");
|
||||
|
||||
assert!(Settings::parse(EMPTY).is_err());
|
||||
assert!(Settings::parse(["test"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -47,7 +44,7 @@ fn two_positionals() {
|
||||
assert_eq!(settings.foo, "a");
|
||||
assert_eq!(settings.bar, "b");
|
||||
|
||||
assert!(Settings::parse(EMPTY).is_err());
|
||||
assert!(Settings::parse(["test"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -65,7 +62,7 @@ fn optional_positional() {
|
||||
foo: Option<String>,
|
||||
}
|
||||
|
||||
let settings = Settings::parse(EMPTY).unwrap();
|
||||
let settings = Settings::parse(["test"]).unwrap();
|
||||
assert_eq!(settings.foo, None);
|
||||
let settings = Settings::parse(["test", "bar"]).unwrap();
|
||||
assert_eq!(settings.foo.unwrap(), "bar");
|
||||
@@ -88,6 +85,50 @@ fn collect_positional() {
|
||||
|
||||
let settings = Settings::parse(["test", "a", "b", "c"]).unwrap();
|
||||
assert_eq!(settings.foo, vec!["a", "b", "c"]);
|
||||
let settings = Settings::parse(EMPTY).unwrap();
|
||||
let settings = Settings::parse(["test"]).unwrap();
|
||||
assert_eq!(settings.foo, Vec::<String>::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn last1() {
|
||||
#[derive(Arguments, Clone)]
|
||||
enum Arg {
|
||||
#[positional(last, ..)]
|
||||
Foo(Vec<String>),
|
||||
}
|
||||
|
||||
#[derive(Default, Options)]
|
||||
#[arg_type(Arg)]
|
||||
struct Settings {
|
||||
#[set(Arg::Foo)]
|
||||
foo: Vec<String>,
|
||||
}
|
||||
|
||||
let settings = Settings::parse(["test", "a", "-b", "c"]).unwrap();
|
||||
assert_eq!(settings.foo, vec!["a", "-b", "c"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn last2() {
|
||||
#[derive(Arguments, Clone)]
|
||||
enum Arg {
|
||||
#[option("-a")]
|
||||
A,
|
||||
|
||||
#[positional(last, ..)]
|
||||
Foo(Vec<String>),
|
||||
}
|
||||
|
||||
#[derive(Default, Options)]
|
||||
#[arg_type(Arg)]
|
||||
struct Settings {
|
||||
#[set(Arg::Foo)]
|
||||
foo: Vec<String>,
|
||||
}
|
||||
|
||||
let settings = Settings::parse(["test", "-a"]).unwrap();
|
||||
assert_eq!(settings.foo, Vec::<String>::new());
|
||||
|
||||
let settings = Settings::parse(["test", "--", "-a"]).unwrap();
|
||||
assert_eq!(settings.foo, vec!["-a"]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user