trailing var args via last keyword

This commit is contained in:
Terts Diepraam
2022-12-20 12:46:36 +01:00
parent 02bceaa5ec
commit 7455f3a3de
3 changed files with 88 additions and 21 deletions
+24 -13
View File
@@ -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)
})
}
+16 -1
View File
@@ -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
View File
@@ -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"]);
}