diff --git a/examples/advanced_struct.rs b/examples/advanced_struct.rs index 856b587..127248c 100644 --- a/examples/advanced_struct.rs +++ b/examples/advanced_struct.rs @@ -5,6 +5,8 @@ // 3) run an example: ./advanced_struct (without parameters) => entered interactive mode // ./advanced_struct --age-full-years 30 --first-name QWE --second-name QWERTY --favorite-color red => // => cli_args: CliArgs { age: Some(30), first_name: Some("QWE"), second_name: Some("QWERTY"), favorite_color: Some(Red) } +// ./advanced_struct --first-name QWE --second-name QWERTY --favorite-color red => +// => cli_args: CliArgs { age: None, first_name: Some("QWE"), second_name: Some("QWERTY"), favorite_color: Some(Red) } // To learn more about the parameters, use "help" flag: ./advanced_struct --help use inquire::Select; @@ -12,13 +14,13 @@ use interactive_clap::{ResultFromCli, ToCliArgs}; use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; #[derive(Debug, Clone, interactive_clap::InteractiveClap)] -#[interactive_clap(skip_default_from_cli)] struct Args { #[interactive_clap(long = "age-full-years")] - #[interactive_clap(skip_default_input_arg)] + #[interactive_clap(skip_interactive_input)] + /// If you want, enter the full age on the command line age: Option, #[interactive_clap(long)] - ///What is your first name? + /// What is your first name? first_name: String, #[interactive_clap(long)] #[interactive_clap(skip_default_input_arg)] @@ -29,67 +31,7 @@ struct Args { favorite_color: ColorPalette, } -impl interactive_clap::FromCli for Args { - type FromCliContext = (); - type FromCliError = color_eyre::eyre::Error; - fn from_cli( - optional_clap_variant: Option<::CliVariant>, - context: Self::FromCliContext, - ) -> ResultFromCli<::CliVariant, Self::FromCliError> - where - Self: Sized + interactive_clap::ToCli, - { - let mut clap_variant = optional_clap_variant.unwrap_or_default(); - if clap_variant.age.is_none() { - clap_variant.age = match Self::input_age(&context) { - Ok(optional_age) => optional_age, - Err(err) => return ResultFromCli::Err(Some(clap_variant), err), - }; - } - let _age = clap_variant.age; - if clap_variant.first_name.is_none() { - clap_variant.first_name = match Self::input_first_name(&context) { - Ok(Some(first_name)) => Some(first_name), - Ok(None) => return ResultFromCli::Cancel(Some(clap_variant)), - Err(err) => return ResultFromCli::Err(Some(clap_variant), err), - }; - } - let _first_name = clap_variant.first_name.clone().expect("Unexpected error"); - if clap_variant.second_name.is_none() { - clap_variant.second_name = match Self::input_second_name(&context) { - Ok(Some(second_name)) => Some(second_name), - Ok(None) => return ResultFromCli::Cancel(Some(clap_variant)), - Err(err) => return ResultFromCli::Err(Some(clap_variant), err), - }; - } - let _second_name = clap_variant.second_name.clone().expect("Unexpected error"); - if clap_variant.favorite_color.is_none() { - clap_variant.favorite_color = match Self::input_favorite_color(&context) { - Ok(Some(favorite_color)) => Some(favorite_color), - Ok(None) => return ResultFromCli::Cancel(Some(clap_variant)), - Err(err) => return ResultFromCli::Err(Some(clap_variant), err), - }; - } - let _favorite_color = clap_variant - .favorite_color - .clone() - .expect("Unexpected error"); - ResultFromCli::Ok(clap_variant) - } -} - impl Args { - fn input_age(_context: &()) -> color_eyre::eyre::Result> { - match inquire::CustomType::new("Input age full years".to_string().as_str()).prompt() { - Ok(value) => Ok(Some(value)), - Err( - inquire::error::InquireError::OperationCanceled - | inquire::error::InquireError::OperationInterrupted, - ) => Ok(None), - Err(err) => Err(err.into()), - } - } - fn input_second_name(_context: &()) -> color_eyre::eyre::Result> { match inquire::Text::new("Input second name".to_string().as_str()).prompt() { Ok(value) => Ok(Some(value)), diff --git a/interactive-clap-derive/src/derives/interactive_clap/methods/from_cli_for_struct.rs b/interactive-clap-derive/src/derives/interactive_clap/methods/from_cli_for_struct.rs index c36104a..cc94d41 100644 --- a/interactive-clap-derive/src/derives/interactive_clap/methods/from_cli_for_struct.rs +++ b/interactive-clap-derive/src/derives/interactive_clap/methods/from_cli_for_struct.rs @@ -99,10 +99,28 @@ pub fn from_cli_for_struct( fn fields_value(field: &syn::Field) -> proc_macro2::TokenStream { let ident_field = &field.clone().ident.expect("this field does not exist"); let fn_input_arg = syn::Ident::new(&format!("input_{}", &ident_field), Span::call_site()); - if field.ty.to_token_stream().to_string() == "bool" { + if field.ty.to_token_stream().to_string() == "bool" + || super::skip_interactive_input::is_skip_interactive_input(field) + { quote! { let #ident_field = clap_variant.#ident_field.clone(); } + } else if field + .ty + .to_token_stream() + .to_string() + .starts_with("Option <") + { + quote! { + if clap_variant.#ident_field.is_none() { + clap_variant + .#ident_field = match Self::#fn_input_arg(&context) { + Ok(optional_field) => optional_field, + Err(err) => return interactive_clap::ResultFromCli::Err(Some(clap_variant), err), + }; + }; + let #ident_field = clap_variant.#ident_field.clone(); + } } else if super::fields_without_subcommand::is_field_without_subcommand(field) { quote! { if clap_variant.#ident_field.is_none() { diff --git a/interactive-clap-derive/src/derives/interactive_clap/methods/input_arg.rs b/interactive-clap-derive/src/derives/interactive_clap/methods/input_arg.rs index 8d82541..d111dd3 100644 --- a/interactive-clap-derive/src/derives/interactive_clap/methods/input_arg.rs +++ b/interactive-clap-derive/src/derives/interactive_clap/methods/input_arg.rs @@ -44,6 +44,10 @@ pub fn vec_fn_input_arg( }; } + if super::skip_interactive_input::is_skip_interactive_input(field) { + return quote! {}; + } + let doc_attrs = field .attrs .iter() diff --git a/interactive-clap-derive/src/derives/interactive_clap/methods/mod.rs b/interactive-clap-derive/src/derives/interactive_clap/methods/mod.rs index 17f11d9..f2ef79b 100644 --- a/interactive-clap-derive/src/derives/interactive_clap/methods/mod.rs +++ b/interactive-clap-derive/src/derives/interactive_clap/methods/mod.rs @@ -6,3 +6,4 @@ pub mod from_cli_for_enum; pub mod from_cli_for_struct; pub mod input_arg; pub mod interactive_clap_attrs_context; +pub mod skip_interactive_input; diff --git a/interactive-clap-derive/src/derives/interactive_clap/methods/skip_interactive_input.rs b/interactive-clap-derive/src/derives/interactive_clap/methods/skip_interactive_input.rs new file mode 100644 index 0000000..627f9b3 --- /dev/null +++ b/interactive-clap-derive/src/derives/interactive_clap/methods/skip_interactive_input.rs @@ -0,0 +1,18 @@ +extern crate proc_macro; + +use syn; + +pub fn is_skip_interactive_input(field: &syn::Field) -> bool { + field + .attrs + .iter() + .filter(|attr| attr.path.is_ident("interactive_clap")) + .flat_map(|attr| attr.tokens.clone()) + .any(|attr_token| match attr_token { + proc_macro2::TokenTree::Group(group) => group + .stream() + .to_string() + .contains("skip_interactive_input"), + _ => false, + }) +}