diff --git a/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_skip_default_from_cli.rs b/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_skip_default_from_cli.rs index c9824e2..d4fdedb 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_skip_default_from_cli.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_skip_default_from_cli.rs @@ -17,16 +17,16 @@ pub fn is_field_without_skip_default_from_cli(field: &syn::Field) -> bool { .filter(|attr_token| match attr_token { proc_macro2::TokenTree::Group(group) => { if group.stream().to_string().contains("skip_default_from_cli") { - false - } else { true + } else { + false } - } - _ => abort_call_site!("Only option `TokenTree::Group` is needed"), - }) + }, + _ => false // abort_call_site!("Only option `TokenTree::Group` is needed") + }) .next() { - Some(token_stream) => true, - None => false, + Some(token_stream) => false, + None => true, } } diff --git a/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_skip_default_input_arg.rs b/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_skip_default_input_arg.rs new file mode 100644 index 0000000..84dd1af --- /dev/null +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_skip_default_input_arg.rs @@ -0,0 +1,36 @@ +extern crate proc_macro; + +use proc_macro_error::abort_call_site; +use quote::quote; +use syn; + +pub fn is_field_without_skip_default_input_arg(field: &syn::Field) -> bool { + if field.attrs.is_empty() { + return true; + } + match field + .attrs + .iter() + .filter(|attr| attr.path.is_ident("interactive_clap".into())) + .map(|attr| attr.tokens.clone()) + .flatten() + .filter(|attr_token| match attr_token { + proc_macro2::TokenTree::Group(group) => { + if group + .stream() + .to_string() + .contains("skip_default_input_arg") + { + true + } else { + false + } + } + _ => false, // abort_call_site!("Only option `TokenTree::Group` is needed") + }) + .next() + { + Some(token_stream) => false, + None => true, + } +} diff --git a/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_subcommand.rs b/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_subcommand.rs index cce9b8f..4d3904f 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_subcommand.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_subcommand.rs @@ -9,23 +9,24 @@ pub fn is_field_without_subcommand(field: &syn::Field) -> bool { return true } match field.attrs.iter() - .filter(|attr| attr.path.is_ident("interactive_clap".into())) - .map(|attr| attr.tokens.clone()) + .map(|attr| { + attr.tokens.clone() + }) .flatten() .filter(|attr_token| { match attr_token { proc_macro2::TokenTree::Group(group) => { if group.stream().to_string().contains("named_arg") || group.stream().to_string().contains("subcommand") { - false - } else { true + } else { + false } }, - _ => abort_call_site!("Only option `TokenTree::Group` is needed") + _ => false // abort_call_site!("Only option `TokenTree::Group` is needed") } }) .next() { - Some(token_stream) => true, - None => false + Some(token_stream) => false, + None => true } } 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 a810b9e..2bbd073 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 @@ -82,7 +82,7 @@ pub fn from_cli_for_struct(ast: &syn::DeriveInput, fields: &syn::Fields) -> proc fn fields_value(field: &syn::Field) -> proc_macro2::TokenStream { let ident_field = &field.clone().ident.expect("this field does not exist"); let fn_from_cli_arg = syn::Ident::new(&format!("from_cli_{}", &ident_field), Span::call_site()); - if field.attrs.is_empty() { + if super::fields_without_subcommand::is_field_without_subcommand(field) { quote! { let #ident_field = Self::#fn_from_cli_arg( optional_clap_variant @@ -90,38 +90,9 @@ fn fields_value(field: &syn::Field) -> proc_macro2::TokenStream { .and_then(|clap_variant| clap_variant.#ident_field), &context, )?; - } - } else { - match field.attrs.iter() - .filter(|attr| attr.path.is_ident("interactive_clap".into())) - .map(|attr| attr.tokens.clone()) - .flatten() - .filter(|attr_token| { - match attr_token { - proc_macro2::TokenTree::Group(group) => { - if group.stream().to_string().contains("named_arg") || group.stream().to_string().contains("subcommand") { - false - } else { - true - } - }, - _ => abort_call_site!("Only option `TokenTree::Group` is needed") - } - }) - .map(|_| { - quote! { - let #ident_field = Self::#fn_from_cli_arg( - optional_clap_variant - .clone() - .and_then(|clap_variant| clap_variant.#ident_field), - &context, - )?; - } - }) - .next() { - Some(token_stream) => token_stream, - None => quote! () } + } else { + quote! () } } 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 new file mode 100644 index 0000000..980dbd0 --- /dev/null +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/input_arg.rs @@ -0,0 +1,76 @@ +extern crate proc_macro; + +use proc_macro2::Span; +use proc_macro_error::abort_call_site; +use quote::{__private::ext::RepToTokensExt, quote}; +use syn; + +pub fn vec_input_arg( + ast: &syn::DeriveInput, + fields: &syn::Fields, +) -> Vec { + let interactive_clap_attrs_context = + super::interactive_clap_attrs_context::InteractiveClapAttrsContext::new(&ast); + if interactive_clap_attrs_context.is_skip_default_from_cli { + return vec![quote!()]; + }; + let vec_fn_input_arg = fields + .iter() + .filter(|field| super::fields_without_subcommand::is_field_without_subcommand(field)) + .filter(|field| { + super::fields_without_skip_default_input_arg::is_field_without_skip_default_input_arg( + field, + ) + }) + .map(|field| { + if field.attrs.is_empty() { + return quote!(); + } + let ident_field = &field.clone().ident.expect("this field does not exist"); + let ty = &field.ty; + + let input_context_dir = interactive_clap_attrs_context + .clone() + .get_input_context_dir(); + + let fn_input_arg = + syn::Ident::new(&format!("input_{}", &ident_field), Span::call_site()); + + let doc_attrs = field + .attrs + .iter() + .filter(|attr| attr.path.is_ident("doc".into())) + .map(|attr| { + let mut literal_string = String::new(); + for attr_token in attr.tokens.clone() { + match attr_token { + proc_macro2::TokenTree::Literal(literal) => { + literal_string = literal.to_string(); + } + _ => (), //abort_call_site!("Only option `TokenTree::Literal` is needed") + } + } + literal_string + }) + .collect::>(); + let literal_vec = doc_attrs + .iter() + .map(|s| s.replace("\"", "")) + .collect::>(); + let literal = proc_macro2::Literal::string(literal_vec.join("\n ").as_str()); + + quote! { + fn #fn_input_arg( + _context: &#input_context_dir, + ) -> color_eyre::eyre::Result<#ty> { + use dialoguer::Input; + Ok(Input::new() + .with_prompt(#literal.to_string().as_str()) + .interact_text()?) + } + } + }) + .filter(|token_stream| !token_stream.is_empty()) + .collect::>(); + vec_fn_input_arg +} 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 19e4013..4294963 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/methods/mod.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/mod.rs @@ -1,8 +1,10 @@ pub mod choose_variant; pub mod cli_field_type; pub mod fields_without_skip_default_from_cli; +pub mod fields_without_skip_default_input_arg; pub mod fields_without_subcommand; pub mod from_cli_for_enum; pub mod from_cli_for_struct; pub mod get_arg_from_cli_for_struct; +pub mod input_arg; pub mod interactive_clap_attrs_context; diff --git a/interactive_clap_derive/src/derives/interactive_clap/mod.rs b/interactive_clap_derive/src/derives/interactive_clap/mod.rs index 7577260..cf049f2 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/mod.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/mod.rs @@ -97,6 +97,8 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream { let fn_get_arg = self::methods::get_arg_from_cli_for_struct::from_cli_arg(&ast, &fields); + let vec_fn_input_arg = self::methods::input_arg::vec_input_arg(&ast, &fields); + let context_scope_fields = fields.iter().map(|field| { context_scope_for_struct_field(field) }) @@ -183,6 +185,7 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream { impl #name { #(#fn_get_arg)* #fn_from_cli_for_struct + #(#vec_fn_input_arg)* fn try_parse() -> Result<#cli_name, clap::Error> { <#cli_name as clap::Clap>::try_parse() @@ -334,37 +337,14 @@ fn context_scope_for_struct(name: &syn::Ident, context_scope_fields: Vec proc_macro2::TokenStream { let ident_field = &field.ident.clone().expect("this field does not exist"); let ty = &field.ty; - if field.attrs.is_empty() { + if self::methods::fields_without_subcommand::is_field_without_subcommand(field) { quote! { pub #ident_field: #ty } } else { - match field.attrs.iter() - .filter(|attr| attr.path.is_ident("interactive_clap".into())) - .map(|attr| attr.tokens.clone()) - .flatten() - .filter(|attr_token| { - match attr_token { - proc_macro2::TokenTree::Group(group) => { - if group.stream().to_string().contains("subcommand") | group.stream().to_string().contains("named_arg") | (group.stream().to_string() == "skip".to_string()) { - false - } else { - true - } - }, - _ => abort_call_site!("Only option `TokenTree::Group` is needed") - } - }) - .map(|_| { - quote! { - pub #ident_field: #ty - } - }) - .next() { - Some(token_stream) => token_stream, - None => quote! () - } + quote! () } + } fn context_scope_for_enum(name: &syn::Ident) -> proc_macro2::TokenStream { diff --git a/src/common.rs b/src/common.rs deleted file mode 100644 index 4ddde44..0000000 --- a/src/common.rs +++ /dev/null @@ -1,33 +0,0 @@ -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumMessage, IntoEnumIterator}; -pub fn prompt_variant(prompt: &str) -> T -where - T: IntoEnumIterator + EnumMessage, - T: Copy + Clone, -{ - let variants = T::iter().collect::>(); - let actions = variants - .iter() - .map(|p| { - p.get_message() - .unwrap_or_else(|| "error[This entry does not have an option message!!]") - .to_owned() - }) - .collect::>(); - - let selected = Select::with_theme(&ColorfulTheme::default()) - .with_prompt(prompt) - .items(&actions) - .default(0) - .interact() - .unwrap(); - - variants[selected] -} - -#[derive(Debug, Clone)] -pub enum ConnectionConfig { - Testnet, - Mainnet, - Betanet, -}