added methods (input_arg())

This commit is contained in:
FroVolod
2022-02-17 12:38:02 +02:00
parent 46865465fd
commit 8b3f9fe17c
8 changed files with 138 additions and 105 deletions

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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
}
}

View File

@@ -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! ()
}
}

View File

@@ -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<proc_macro2::TokenStream> {
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::<Vec<_>>();
let literal_vec = doc_attrs
.iter()
.map(|s| s.replace("\"", ""))
.collect::<Vec<_>>();
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<proc_macro2::TokenStream>>();
vec_fn_input_arg
}

View File

@@ -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;

View File

@@ -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_ma
fn context_scope_for_struct_field(field: &syn::Field) -> 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 {

View File

@@ -1,33 +0,0 @@
use dialoguer::{theme::ColorfulTheme, Select};
use strum::{EnumMessage, IntoEnumIterator};
pub fn prompt_variant<T>(prompt: &str) -> T
where
T: IntoEnumIterator + EnumMessage,
T: Copy + Clone,
{
let variants = T::iter().collect::<Vec<_>>();
let actions = variants
.iter()
.map(|p| {
p.get_message()
.unwrap_or_else(|| "error[This entry does not have an option message!!]")
.to_owned()
})
.collect::<Vec<_>>();
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,
}