diff --git a/argh/src/lib.rs b/argh/src/lib.rs index b191459..3cbfa5f 100644 --- a/argh/src/lib.rs +++ b/argh/src/lib.rs @@ -833,6 +833,14 @@ impl ParseValueSlot for ParseValueSlotTy, T> { } } +// `ParseValueSlotTy>, T>` is used as the slot for optional repeating arguments. +impl ParseValueSlot for ParseValueSlotTy>, T> { + fn fill_slot(&mut self, arg: &str, value: &str) -> Result<(), String> { + self.slot.get_or_insert_with(Vec::new).push((self.parse_func)(arg, value)?); + Ok(()) + } +} + /// A type which can be the receiver of a `Flag`. pub trait Flag { /// Creates a default instance of the flag value; diff --git a/argh_derive/src/args_info.rs b/argh_derive/src/args_info.rs index ae640a7..7341965 100644 --- a/argh_derive/src/args_info.rs +++ b/argh_derive/src/args_info.rs @@ -234,10 +234,10 @@ fn impl_args_info_data<'a>( Optionality::None => quote! { argh::Optionality::Required }, Optionality::Defaulted(_) => quote! { argh::Optionality::Optional }, Optionality::Optional => quote! { argh::Optionality::Optional }, - Optionality::Repeating if field.attrs.greedy.is_some() => { + Optionality::Repeating | Optionality::DefaultedRepeating(_) if field.attrs.greedy.is_some() => { quote! { argh::Optionality::Greedy } } - Optionality::Repeating => quote! { argh::Optionality::Repeating }, + Optionality::Repeating | Optionality::DefaultedRepeating(_) => quote! { argh::Optionality::Repeating }, }; match field.kind { diff --git a/argh_derive/src/help.rs b/argh_derive/src/help.rs index bd20622..4022614 100644 --- a/argh_derive/src/help.rs +++ b/argh_derive/src/help.rs @@ -157,7 +157,7 @@ fn positional_usage(out: &mut String, field: &StructField<'_>) { } let name = field.positional_arg_name(); out.push_str(&name); - if field.optionality == Optionality::Repeating { + if matches!(field.optionality, Optionality::Repeating | Optionality::DefaultedRepeating(_)) { out.push_str("..."); } if field.attrs.greedy.is_none() { @@ -194,7 +194,7 @@ fn option_usage(out: &mut String, field: &StructField<'_>) { } else { out.push_str(long_name.trim_start_matches("--")); } - if field.optionality == Optionality::Repeating { + if matches!(field.optionality, Optionality::Repeating | Optionality::DefaultedRepeating(_)) { out.push_str("..."); } out.push('>'); diff --git a/argh_derive/src/lib.rs b/argh_derive/src/lib.rs index 7e12542..bb2cb28 100644 --- a/argh_derive/src/lib.rs +++ b/argh_derive/src/lib.rs @@ -67,6 +67,7 @@ enum Optionality { Defaulted(TokenStream), Optional, Repeating, + DefaultedRepeating(TokenStream), } impl PartialEq for Optionality { @@ -159,8 +160,14 @@ impl<'a> StructField<'a> { tree }) .collect(); - optionality = Optionality::Defaulted(tokens); - ty_without_wrapper = &field.ty; + let inner = if let Some(x) = ty_inner(&["Vec"], &field.ty) { + optionality = Optionality::DefaultedRepeating(tokens); + x + } else { + optionality = Optionality::Defaulted(tokens); + &field.ty + }; + ty_without_wrapper = inner; } else { let mut inner = None; optionality = if let Some(x) = ty_inner(&["Option"], &field.ty) { @@ -653,6 +660,9 @@ fn declare_local_storage_for_from_args_fields<'a>( Optionality::None | Optionality::Defaulted(_) => { quote! { std::option::Option<#field_type> } } + Optionality::DefaultedRepeating(_) => { + quote! { std::option::Option> } + } }; match field.kind { @@ -698,7 +708,7 @@ fn unwrap_from_args_fields<'a>( Optionality::Optional | Optionality::Repeating => { quote! { #field_name: #field_name.slot } } - Optionality::Defaulted(tokens) => { + Optionality::Defaulted(tokens) | Optionality::DefaultedRepeating(tokens) => { quote! { #field_name: #field_name.slot.unwrap_or_else(|| #tokens) } @@ -708,7 +718,7 @@ fn unwrap_from_args_fields<'a>( FieldKind::SubCommand => match field.optionality { Optionality::None => quote! { #field_name: #field_name.unwrap() }, Optionality::Optional | Optionality::Repeating => field_name.into_token_stream(), - Optionality::Defaulted(_) => unreachable!(), + Optionality::Defaulted(_) | Optionality::DefaultedRepeating(_) => unreachable!(), }, } }) @@ -738,6 +748,9 @@ fn declare_local_storage_for_redacted_fields<'a>( Optionality::Repeating => { quote! { std::vec::Vec } } + Optionality::DefaultedRepeating(_) => { + quote! { std::option::Option> } + } Optionality::None | Optionality::Optional | Optionality::Defaulted(_) => { quote! { std::option::Option } } @@ -756,6 +769,9 @@ fn declare_local_storage_for_redacted_fields<'a>( Optionality::Repeating => { quote! { std::vec::Vec } } + Optionality::DefaultedRepeating(_) => { + quote! { std::option::Option> } + } Optionality::None | Optionality::Optional | Optionality::Defaulted(_) => { quote! { std::option::Option } } @@ -798,6 +814,13 @@ fn unwrap_redacted_fields<'a>( __redacted.extend(#field_name.slot.into_iter()); } } + Optionality::DefaultedRepeating(_) => { + quote! { + if let Some(__field_name) = #field_name.slot { + __redacted.extend(__field_name.into_iter()); + } + } + } Optionality::None | Optionality::Optional | Optionality::Defaulted(_) => { quote! { if let Some(__field_name) = #field_name.slot { diff --git a/argh_shared/src/lib.rs b/argh_shared/src/lib.rs index 20383a4..c9306e0 100644 --- a/argh_shared/src/lib.rs +++ b/argh_shared/src/lib.rs @@ -81,7 +81,7 @@ pub struct FlagInfo<'a> { /// The long string of the flag. pub long: &'a str, /// The single character short indicator - /// for trhis flag. + /// for this flag. pub short: Option, /// The description of the flag. pub description: &'a str, @@ -102,7 +102,7 @@ pub enum FlagInfoKind<'a> { Option { arg_name: &'a str }, } -/// The optionality defines the requirments related +/// The optionality defines the requirements related /// to the presence of the argument on the command line. #[derive(Debug, Default, PartialEq, Eq, serde::Serialize)] pub enum Optionality { @@ -117,7 +117,7 @@ pub enum Optionality { /// or more times. Repeating, /// Greedy is used for positional arguments which - /// capture the all command line input upto the next flag or + /// capture the all command line input up to the next flag or /// the end of the input. Greedy, }