add options_first argument for the arguments attribute

This commit is contained in:
Terts Diepraam
2024-01-10 13:07:41 +01:00
committed by Sylvestre Ledru
parent 50bd56675b
commit 5c7bf4e3a7
4 changed files with 86 additions and 1 deletions
+5
View File
@@ -13,6 +13,7 @@ pub struct ArgumentsAttr {
pub file: Option<String>,
pub exit_code: i32,
pub parse_echo_style: bool,
pub options_first: bool,
}
impl Default for ArgumentsAttr {
@@ -23,6 +24,7 @@ impl Default for ArgumentsAttr {
file: None,
exit_code: 1,
parse_echo_style: false,
options_first: false,
}
}
}
@@ -55,6 +57,9 @@ impl ArgumentsAttr {
"parse_echo_style" => {
args.parse_echo_style = true;
}
"options_first" => {
args.options_first = true;
}
_ => return Err(meta.error("unrecognized argument for arguments attribute")),
};
Ok(())
+15 -1
View File
@@ -67,6 +67,20 @@ pub fn arguments(input: TokenStream) -> TokenStream {
quote!(parser.next()?)
};
// If options_first is set and we find the first positional argument, we
// immediately return all of them.
let positional = if arguments_attr.options_first {
quote!(
// Unwrap is fine because this is called when we have just parsed a
// value and therefore are not partially within an option.
let mut values = parser.raw_args().unwrap().collect::<Vec<OsString>>();
values.insert(0, value);
Ok(Some(::uutils_args::Argument::MultiPositional(values)))
)
} else {
quote!(Ok(Some(::uutils_args::Argument::Positional(value))))
};
let expanded = quote!(
impl #impl_generics Arguments for #name #ty_generics #where_clause {
const EXIT_CODE: i32 = #exit_code;
@@ -91,7 +105,7 @@ pub fn arguments(input: TokenStream) -> TokenStream {
match arg {
lexopt::Arg::Short(short) => { #short },
lexopt::Arg::Long(long) => { #long },
lexopt::Arg::Value(value) => { Ok(Some(::uutils_args::Argument::Positional(value))) },
lexopt::Arg::Value(value) => { #positional },
}
}
+4
View File
@@ -65,6 +65,7 @@ pub enum Argument<T: Arguments> {
Help,
Version,
Positional(OsString),
MultiPositional(Vec<OsString>),
Custom(T),
}
@@ -143,6 +144,9 @@ impl<T: Arguments> ArgumentIter<T> {
Argument::Positional(arg) => {
self.positional_arguments.push(arg);
}
Argument::MultiPositional(args) => {
self.positional_arguments.extend(args);
}
Argument::Custom(arg) => return Ok(Some(arg)),
}
}
+62
View File
@@ -0,0 +1,62 @@
use std::ffi::OsString;
use uutils_args::{Arguments, Options};
#[test]
fn timeout_like() {
// The timeout coreutil has -v and a command argument
#[derive(Arguments)]
#[arguments(options_first)]
enum Arg {
#[arg("-v", "--verbose")]
Verbose,
}
#[derive(Default)]
struct Settings {
verbose: bool,
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
match arg {
Arg::Verbose => self.verbose = true,
}
}
}
let (settings, command) = Settings::default()
.parse(["timeout", "-v", "10", "foo", "-v"])
.unwrap();
assert!(settings.verbose);
assert_eq!(
command,
vec![
OsString::from("10"),
OsString::from("foo"),
OsString::from("-v")
]
);
let (settings, command) = Settings::default()
.parse(["timeout", "10", "foo", "-v"])
.unwrap();
assert!(!settings.verbose);
assert_eq!(
command,
vec![
OsString::from("10"),
OsString::from("foo"),
OsString::from("-v")
]
);
let (settings, command) = Settings::default()
.parse(["timeout", "--", "10", "-v"])
.unwrap();
assert!(!settings.verbose);
assert_eq!(command, vec![OsString::from("10"), OsString::from("-v")]);
}