mirror of
https://github.com/Drop-OSS/interactive-clap.git
synced 2026-01-30 20:55:25 +01:00
feat: Added support for "#[interactive_clap(flatten)]" (#15)
Co-authored-by: FroVolod <frol_off@meta.ua>
This commit is contained in:
61
examples/struct_with_flatten.rs
Normal file
61
examples/struct_with_flatten.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
// This example shows additional functionality of the "interactive-clap" macro for parsing command-line data into a structure using the macro's flatten attributes.
|
||||
|
||||
// 1) build an example: cargo build --example struct_with_flatten
|
||||
// 2) go to the `examples` folder: cd target/debug/examples
|
||||
// 3) run an example: ./struct_with_flatten (without parameters) => entered interactive mode
|
||||
// ./struct_with_flatten QWERTY 18 => account: CliAccount { social_db_folder: None, account: Some(CliSender { sender_account_id: Some("QWERTY"), age: Some(18) }) }
|
||||
// To learn more about the parameters, use "help" flag: ./struct_with_flatten --help
|
||||
|
||||
use interactive_clap::{ResultFromCli, ToCliArgs};
|
||||
|
||||
#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
|
||||
struct Account {
|
||||
/// Change SocialDb prefix
|
||||
#[interactive_clap(long)]
|
||||
#[interactive_clap(skip_interactive_input)]
|
||||
social_db_folder: Option<String>,
|
||||
#[interactive_clap(flatten)]
|
||||
account: Sender,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
|
||||
pub struct Sender {
|
||||
/// What is the sender account ID?
|
||||
pub sender_account_id: String,
|
||||
/// How old is the sender?
|
||||
pub age: u64,
|
||||
}
|
||||
|
||||
fn main() -> color_eyre::Result<()> {
|
||||
let mut cli_account = Account::parse();
|
||||
let context = (); // default: input_context = ()
|
||||
loop {
|
||||
let account = <Account as interactive_clap::FromCli>::from_cli(Some(cli_account), context);
|
||||
match account {
|
||||
ResultFromCli::Ok(cli_account) | ResultFromCli::Cancel(Some(cli_account)) => {
|
||||
println!("account: {cli_account:?}");
|
||||
println!(
|
||||
"Your console command: {}",
|
||||
shell_words::join(&cli_account.to_cli_args())
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
ResultFromCli::Cancel(None) => {
|
||||
println!("Goodbye!");
|
||||
return Ok(());
|
||||
}
|
||||
ResultFromCli::Back => {
|
||||
cli_account = Default::default();
|
||||
}
|
||||
ResultFromCli::Err(cli_account, err) => {
|
||||
if let Some(cli_account) = cli_account {
|
||||
println!(
|
||||
"Your console command: {}",
|
||||
shell_words::join(&cli_account.to_cli_args())
|
||||
);
|
||||
}
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use syn;
|
||||
|
||||
pub fn is_field_with_flatten(field: &syn::Field) -> bool {
|
||||
if field.attrs.is_empty() {
|
||||
return false;
|
||||
}
|
||||
field
|
||||
.attrs
|
||||
.iter()
|
||||
.flat_map(|attr| attr.tokens.clone())
|
||||
.any(|attr_token| attr_token.to_string().contains("flatten"))
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use syn;
|
||||
|
||||
pub fn is_field_with_skip_default_input_arg(field: &syn::Field) -> bool {
|
||||
if field.attrs.is_empty() {
|
||||
return false;
|
||||
}
|
||||
field
|
||||
.attrs
|
||||
.iter()
|
||||
.filter(|attr| attr.path.is_ident("interactive_clap"))
|
||||
.flat_map(|attr| attr.tokens.clone())
|
||||
.any(|attr_token| {
|
||||
attr_token.to_string().contains("skip_default_input_arg")
|
||||
|| attr_token.to_string().contains("flatten")
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use syn;
|
||||
|
||||
pub fn is_field_with_subcommand(field: &syn::Field) -> bool {
|
||||
if field.attrs.is_empty() {
|
||||
return false;
|
||||
}
|
||||
field
|
||||
.attrs
|
||||
.iter()
|
||||
.flat_map(|attr| attr.tokens.clone())
|
||||
.any(|attr_token| {
|
||||
attr_token.to_string().contains("named_arg")
|
||||
|| attr_token.to_string().contains("subcommand")
|
||||
})
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
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"))
|
||||
.flat_map(|attr| attr.tokens.clone())
|
||||
.find(|attr_token| match attr_token {
|
||||
proc_macro2::TokenTree::Group(group) => group
|
||||
.stream()
|
||||
.to_string()
|
||||
.contains("skip_default_input_arg"),
|
||||
_ => false, // abort_call_site!("Only option `TokenTree::Group` is needed")
|
||||
}) {
|
||||
Some(_token_stream) => false,
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use syn;
|
||||
|
||||
pub fn is_field_without_subcommand(field: &syn::Field) -> bool {
|
||||
if field.attrs.is_empty() {
|
||||
return true;
|
||||
}
|
||||
match field
|
||||
.attrs
|
||||
.iter()
|
||||
.flat_map(|attr| attr.tokens.clone())
|
||||
.find(|attr_token| {
|
||||
match attr_token {
|
||||
proc_macro2::TokenTree::Group(group) => {
|
||||
group.stream().to_string().contains("named_arg")
|
||||
|| group.stream().to_string().contains("subcommand")
|
||||
}
|
||||
_ => false, // abort_call_site!("Only option `TokenTree::Group` is needed")
|
||||
}
|
||||
}) {
|
||||
Some(_token_stream) => false,
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,12 @@ pub fn from_cli_for_struct(
|
||||
return quote!();
|
||||
};
|
||||
|
||||
let fields_without_subcommand = fields
|
||||
let fields_without_subcommand_and_flatten = fields
|
||||
.iter()
|
||||
.filter(|field| super::fields_without_subcommand::is_field_without_subcommand(field))
|
||||
.filter(|field| {
|
||||
!super::fields_with_subcommand::is_field_with_subcommand(field)
|
||||
&& !super::fields_with_flatten::is_field_with_flatten(field)
|
||||
})
|
||||
.map(|field| {
|
||||
let ident_field = &field.clone().ident.expect("this field does not exist");
|
||||
quote! {#ident_field: #ident_field.into()}
|
||||
@@ -31,25 +34,23 @@ pub fn from_cli_for_struct(
|
||||
.map(fields_value)
|
||||
.filter(|token_stream| !token_stream.is_empty());
|
||||
|
||||
let field_value_named_arg = if let Some(token_stream) = fields
|
||||
let field_value_named_arg = fields
|
||||
.iter()
|
||||
.map(|field| field_value_named_arg(name, field))
|
||||
.find(|token_stream| !token_stream.is_empty())
|
||||
{
|
||||
token_stream
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
.unwrap_or(quote!());
|
||||
|
||||
let field_value_subcommand = if let Some(token_stream) = fields
|
||||
let field_value_subcommand = fields
|
||||
.iter()
|
||||
.map(field_value_subcommand)
|
||||
.find(|token_stream| !token_stream.is_empty())
|
||||
{
|
||||
token_stream
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
.unwrap_or(quote!());
|
||||
|
||||
let field_value_flatten = fields
|
||||
.iter()
|
||||
.map(field_value_flatten)
|
||||
.find(|token_stream| !token_stream.is_empty())
|
||||
.unwrap_or(quote!());
|
||||
|
||||
let input_context_dir = interactive_clap_attrs_context
|
||||
.clone()
|
||||
@@ -60,7 +61,7 @@ pub fn from_cli_for_struct(
|
||||
Span::call_site(),
|
||||
);
|
||||
let new_context_scope = quote! {
|
||||
let new_context_scope = #interactive_clap_context_scope_for_struct { #(#fields_without_subcommand,)* };
|
||||
let new_context_scope = #interactive_clap_context_scope_for_struct { #(#fields_without_subcommand_and_flatten,)* };
|
||||
};
|
||||
|
||||
let output_context = match &interactive_clap_attrs_context.output_context_dir {
|
||||
@@ -84,10 +85,11 @@ pub fn from_cli_for_struct(
|
||||
optional_clap_variant: Option<<Self as interactive_clap::ToCli>::CliVariant>,
|
||||
context: Self::FromCliContext,
|
||||
) -> interactive_clap::ResultFromCli<<Self as interactive_clap::ToCli>::CliVariant, Self::FromCliError> where Self: Sized + interactive_clap::ToCli {
|
||||
let mut clap_variant = optional_clap_variant.unwrap_or_default();
|
||||
let mut clap_variant = optional_clap_variant.clone().unwrap_or_default();
|
||||
#(#fields_value)*
|
||||
#new_context_scope
|
||||
#output_context
|
||||
#field_value_flatten
|
||||
#field_value_named_arg
|
||||
#field_value_subcommand;
|
||||
interactive_clap::ResultFromCli::Ok(clap_variant)
|
||||
@@ -105,6 +107,8 @@ fn fields_value(field: &syn::Field) -> proc_macro2::TokenStream {
|
||||
quote! {
|
||||
let #ident_field = clap_variant.#ident_field.clone();
|
||||
}
|
||||
} else if super::fields_with_flatten::is_field_with_flatten(field) {
|
||||
quote!()
|
||||
} else if field
|
||||
.ty
|
||||
.to_token_stream()
|
||||
@@ -121,7 +125,7 @@ fn fields_value(field: &syn::Field) -> proc_macro2::TokenStream {
|
||||
};
|
||||
let #ident_field = clap_variant.#ident_field.clone();
|
||||
}
|
||||
} else if super::fields_without_subcommand::is_field_without_subcommand(field) {
|
||||
} else if !super::fields_with_subcommand::is_field_with_subcommand(field) {
|
||||
quote! {
|
||||
if clap_variant.#ident_field.is_none() {
|
||||
clap_variant
|
||||
@@ -144,56 +148,54 @@ fn field_value_named_arg(name: &syn::Ident, field: &syn::Field) -> proc_macro2::
|
||||
if field.attrs.is_empty() {
|
||||
quote!()
|
||||
} else {
|
||||
match field.attrs.iter()
|
||||
.filter(|attr| attr.path.is_ident("interactive_clap"))
|
||||
.flat_map(|attr| attr.tokens.clone())
|
||||
.filter(|attr_token| {
|
||||
match attr_token {
|
||||
proc_macro2::TokenTree::Group(group) => group.stream().to_string().contains("named_arg"),
|
||||
_ => abort_call_site!("Only option `TokenTree::Group` is needed")
|
||||
}
|
||||
})
|
||||
.map(|_| {
|
||||
let type_string = match ty {
|
||||
syn::Type::Path(type_path) => {
|
||||
match type_path.path.segments.last() {
|
||||
Some(path_segment) => path_segment.ident.to_string(),
|
||||
_ => String::new()
|
||||
}
|
||||
},
|
||||
_ => String::new()
|
||||
};
|
||||
let enum_for_clap_named_arg = syn::Ident::new(&format!("ClapNamedArg{}For{}", &type_string, &name), Span::call_site());
|
||||
let variant_name_string = crate::helpers::snake_case_to_camel_case::snake_case_to_camel_case(ident_field.to_string());
|
||||
let variant_name = &syn::Ident::new(&variant_name_string, Span::call_site());
|
||||
quote! {
|
||||
let optional_field = match clap_variant.#ident_field.take() {
|
||||
Some(#enum_for_clap_named_arg::#variant_name(cli_arg)) => Some(cli_arg),
|
||||
None => None,
|
||||
field.attrs.iter()
|
||||
.filter(|attr| attr.path.is_ident("interactive_clap"))
|
||||
.flat_map(|attr| attr.tokens.clone())
|
||||
.filter(|attr_token| {
|
||||
match attr_token {
|
||||
proc_macro2::TokenTree::Group(group) => group.stream().to_string().contains("named_arg"),
|
||||
_ => abort_call_site!("Only option `TokenTree::Group` is needed")
|
||||
}
|
||||
})
|
||||
.map(|_| {
|
||||
let type_string = match ty {
|
||||
syn::Type::Path(type_path) => {
|
||||
match type_path.path.segments.last() {
|
||||
Some(path_segment) => path_segment.ident.to_string(),
|
||||
_ => String::new()
|
||||
}
|
||||
},
|
||||
_ => String::new()
|
||||
};
|
||||
match <#ty as interactive_clap::FromCli>::from_cli(
|
||||
optional_field,
|
||||
context.into(),
|
||||
) {
|
||||
interactive_clap::ResultFromCli::Ok(cli_field) => {
|
||||
clap_variant.#ident_field = Some(#enum_for_clap_named_arg::#variant_name(cli_field));
|
||||
}
|
||||
interactive_clap::ResultFromCli::Cancel(optional_cli_field) => {
|
||||
clap_variant.#ident_field = optional_cli_field.map(#enum_for_clap_named_arg::#variant_name);
|
||||
return interactive_clap::ResultFromCli::Cancel(Some(clap_variant));
|
||||
}
|
||||
interactive_clap::ResultFromCli::Back => return interactive_clap::ResultFromCli::Back,
|
||||
interactive_clap::ResultFromCli::Err(optional_cli_field, err) => {
|
||||
clap_variant.#ident_field = optional_cli_field.map(#enum_for_clap_named_arg::#variant_name);
|
||||
return interactive_clap::ResultFromCli::Err(Some(clap_variant), err);
|
||||
let enum_for_clap_named_arg = syn::Ident::new(&format!("ClapNamedArg{}For{}", &type_string, &name), Span::call_site());
|
||||
let variant_name_string = crate::helpers::snake_case_to_camel_case::snake_case_to_camel_case(ident_field.to_string());
|
||||
let variant_name = &syn::Ident::new(&variant_name_string, Span::call_site());
|
||||
quote! {
|
||||
let optional_field = match clap_variant.#ident_field.take() {
|
||||
Some(#enum_for_clap_named_arg::#variant_name(cli_arg)) => Some(cli_arg),
|
||||
None => None,
|
||||
};
|
||||
match <#ty as interactive_clap::FromCli>::from_cli(
|
||||
optional_field,
|
||||
context.into(),
|
||||
) {
|
||||
interactive_clap::ResultFromCli::Ok(cli_field) => {
|
||||
clap_variant.#ident_field = Some(#enum_for_clap_named_arg::#variant_name(cli_field));
|
||||
}
|
||||
interactive_clap::ResultFromCli::Cancel(optional_cli_field) => {
|
||||
clap_variant.#ident_field = optional_cli_field.map(#enum_for_clap_named_arg::#variant_name);
|
||||
return interactive_clap::ResultFromCli::Cancel(Some(clap_variant));
|
||||
}
|
||||
interactive_clap::ResultFromCli::Back => return interactive_clap::ResultFromCli::Back,
|
||||
interactive_clap::ResultFromCli::Err(optional_cli_field, err) => {
|
||||
clap_variant.#ident_field = optional_cli_field.map(#enum_for_clap_named_arg::#variant_name);
|
||||
return interactive_clap::ResultFromCli::Err(Some(clap_variant), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.next() {
|
||||
Some(token_stream) => token_stream,
|
||||
None => quote! ()
|
||||
}
|
||||
})
|
||||
.next()
|
||||
.unwrap_or(quote!())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,40 +205,77 @@ fn field_value_subcommand(field: &syn::Field) -> proc_macro2::TokenStream {
|
||||
if field.attrs.is_empty() {
|
||||
quote!()
|
||||
} else {
|
||||
match field.attrs.iter()
|
||||
.filter(|attr| attr.path.is_ident("interactive_clap"))
|
||||
.flat_map(|attr| attr.tokens.clone())
|
||||
.filter(|attr_token| {
|
||||
match attr_token {
|
||||
proc_macro2::TokenTree::Group(group) => group.stream().to_string().contains("subcommand"),
|
||||
_ => abort_call_site!("Only option `TokenTree::Group` is needed")
|
||||
}
|
||||
})
|
||||
.map(|_| {
|
||||
quote! {
|
||||
match <#ty as interactive_clap::FromCli>::from_cli(clap_variant.#ident_field.take(), context.into()) {
|
||||
interactive_clap::ResultFromCli::Ok(cli_field) => {
|
||||
clap_variant.#ident_field = Some(cli_field);
|
||||
}
|
||||
interactive_clap::ResultFromCli::Cancel(option_cli_field) => {
|
||||
clap_variant.#ident_field = option_cli_field;
|
||||
return interactive_clap::ResultFromCli::Cancel(Some(clap_variant));
|
||||
}
|
||||
interactive_clap::ResultFromCli::Cancel(option_cli_field) => {
|
||||
clap_variant.#ident_field = option_cli_field;
|
||||
return interactive_clap::ResultFromCli::Cancel(Some(clap_variant));
|
||||
}
|
||||
interactive_clap::ResultFromCli::Back => return interactive_clap::ResultFromCli::Back,
|
||||
interactive_clap::ResultFromCli::Err(option_cli_field, err) => {
|
||||
clap_variant.#ident_field = option_cli_field;
|
||||
return interactive_clap::ResultFromCli::Err(Some(clap_variant), err);
|
||||
field.attrs.iter()
|
||||
.filter(|attr| attr.path.is_ident("interactive_clap"))
|
||||
.flat_map(|attr| attr.tokens.clone())
|
||||
.filter(|attr_token| {
|
||||
match attr_token {
|
||||
proc_macro2::TokenTree::Group(group) => group.stream().to_string().contains("subcommand"),
|
||||
_ => abort_call_site!("Only option `TokenTree::Group` is needed")
|
||||
}
|
||||
})
|
||||
.map(|_| {
|
||||
quote! {
|
||||
match <#ty as interactive_clap::FromCli>::from_cli(clap_variant.#ident_field.take(), context.into()) {
|
||||
interactive_clap::ResultFromCli::Ok(cli_field) => {
|
||||
clap_variant.#ident_field = Some(cli_field);
|
||||
}
|
||||
interactive_clap::ResultFromCli::Cancel(option_cli_field) => {
|
||||
clap_variant.#ident_field = option_cli_field;
|
||||
return interactive_clap::ResultFromCli::Cancel(Some(clap_variant));
|
||||
}
|
||||
interactive_clap::ResultFromCli::Cancel(option_cli_field) => {
|
||||
clap_variant.#ident_field = option_cli_field;
|
||||
return interactive_clap::ResultFromCli::Cancel(Some(clap_variant));
|
||||
}
|
||||
interactive_clap::ResultFromCli::Back => return interactive_clap::ResultFromCli::Back,
|
||||
interactive_clap::ResultFromCli::Err(option_cli_field, err) => {
|
||||
clap_variant.#ident_field = option_cli_field;
|
||||
return interactive_clap::ResultFromCli::Err(Some(clap_variant), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.next() {
|
||||
Some(token_stream) => token_stream,
|
||||
None => quote! ()
|
||||
}
|
||||
})
|
||||
.next()
|
||||
.unwrap_or(quote!())
|
||||
}
|
||||
}
|
||||
|
||||
fn field_value_flatten(field: &syn::Field) -> proc_macro2::TokenStream {
|
||||
let ident_field = &field.clone().ident.expect("this field does not exist");
|
||||
let ty = &field.ty;
|
||||
if field.attrs.is_empty() {
|
||||
quote!()
|
||||
} else {
|
||||
field.attrs.iter()
|
||||
.filter(|attr| attr.path.is_ident("interactive_clap"))
|
||||
.flat_map(|attr| attr.tokens.clone())
|
||||
.filter(|attr_token| {
|
||||
match attr_token {
|
||||
proc_macro2::TokenTree::Group(group) => group.stream().to_string().contains("flatten"),
|
||||
_ => abort_call_site!("Only option `TokenTree::Group` is needed")
|
||||
}
|
||||
})
|
||||
.map(|_| {
|
||||
quote! {
|
||||
match #ty::from_cli(
|
||||
optional_clap_variant.unwrap_or_default().#ident_field,
|
||||
context.into(),
|
||||
) {
|
||||
interactive_clap::ResultFromCli::Ok(cli_field) => clap_variant.#ident_field = Some(cli_field),
|
||||
interactive_clap::ResultFromCli::Cancel(optional_cli_field) => {
|
||||
clap_variant.#ident_field = optional_cli_field;
|
||||
return interactive_clap::ResultFromCli::Cancel(Some(clap_variant));
|
||||
}
|
||||
interactive_clap::ResultFromCli::Back => return interactive_clap::ResultFromCli::Back,
|
||||
interactive_clap::ResultFromCli::Err(optional_cli_field, err) => {
|
||||
clap_variant.#ident_field = optional_cli_field;
|
||||
return interactive_clap::ResultFromCli::Err(Some(clap_variant), err);
|
||||
}
|
||||
};
|
||||
}
|
||||
})
|
||||
.next()
|
||||
.unwrap_or(quote!())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ pub fn vec_fn_input_arg(
|
||||
super::interactive_clap_attrs_context::InteractiveClapAttrsContext::new(ast);
|
||||
let vec_fn_input_arg = fields
|
||||
.iter()
|
||||
.filter(|field| super::fields_without_subcommand::is_field_without_subcommand(field))
|
||||
.filter(|field| !super::fields_with_subcommand::is_field_with_subcommand(field))
|
||||
.filter(|field| {
|
||||
super::fields_without_skip_default_input_arg::is_field_without_skip_default_input_arg(
|
||||
!super::fields_with_skip_default_input_arg::is_field_with_skip_default_input_arg(
|
||||
field,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
pub mod choose_variant;
|
||||
pub mod cli_field_type;
|
||||
pub mod fields_without_skip_default_input_arg;
|
||||
pub mod fields_without_subcommand;
|
||||
pub mod fields_with_flatten;
|
||||
pub mod fields_with_skip_default_input_arg;
|
||||
pub mod fields_with_subcommand;
|
||||
pub mod from_cli_for_enum;
|
||||
pub mod from_cli_for_struct;
|
||||
pub mod input_arg;
|
||||
|
||||
@@ -121,56 +121,52 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream {
|
||||
.collect::<Vec<_>>();
|
||||
let context_scope_for_struct = context_scope_for_struct(name, context_scope_fields);
|
||||
|
||||
let clap_enum_for_named_arg =
|
||||
if let Some(token_stream) = fields.iter().find_map(|field| {
|
||||
let ident_field = &field.clone().ident.expect("this field does not exist");
|
||||
let variant_name_string = crate::helpers::snake_case_to_camel_case::snake_case_to_camel_case(ident_field.to_string());
|
||||
let variant_name = &syn::Ident::new(&variant_name_string, Span::call_site());
|
||||
let attr_doc_vec: Vec<_> = field.attrs.iter()
|
||||
.filter(|attr| attr.path.is_ident("doc"))
|
||||
.map(|attr| attr.into_token_stream())
|
||||
.collect();
|
||||
field.attrs.iter()
|
||||
.filter(|attr| attr.path.is_ident("interactive_clap"))
|
||||
.flat_map(|attr| attr.tokens.clone())
|
||||
.filter(|attr_token| {
|
||||
match attr_token {
|
||||
proc_macro2::TokenTree::Group(group) => group.stream().to_string().contains("named_arg"),
|
||||
_ => abort_call_site!("Only option `TokenTree::Group` is needed")
|
||||
}
|
||||
})
|
||||
.map(|_| {
|
||||
let ty = &field.ty;
|
||||
let type_string = match ty {
|
||||
syn::Type::Path(type_path) => {
|
||||
match type_path.path.segments.last() {
|
||||
Some(path_segment) => path_segment.ident.to_string(),
|
||||
_ => String::new()
|
||||
}
|
||||
},
|
||||
_ => String::new()
|
||||
};
|
||||
let enum_for_clap_named_arg = syn::Ident::new(&format!("ClapNamedArg{}For{}", &type_string, &name), Span::call_site());
|
||||
quote! {
|
||||
#[derive(Debug, Clone, clap::Parser, interactive_clap_derive::ToCliArgs)]
|
||||
pub enum #enum_for_clap_named_arg {
|
||||
#(#attr_doc_vec)*
|
||||
#variant_name(<#ty as interactive_clap::ToCli>::CliVariant)
|
||||
let clap_enum_for_named_arg = fields.iter().find_map(|field| {
|
||||
let ident_field = &field.clone().ident.expect("this field does not exist");
|
||||
let variant_name_string = crate::helpers::snake_case_to_camel_case::snake_case_to_camel_case(ident_field.to_string());
|
||||
let variant_name = &syn::Ident::new(&variant_name_string, Span::call_site());
|
||||
let attr_doc_vec: Vec<_> = field.attrs.iter()
|
||||
.filter(|attr| attr.path.is_ident("doc"))
|
||||
.map(|attr| attr.into_token_stream())
|
||||
.collect();
|
||||
field.attrs.iter()
|
||||
.filter(|attr| attr.path.is_ident("interactive_clap"))
|
||||
.flat_map(|attr| attr.tokens.clone())
|
||||
.filter(|attr_token| {
|
||||
match attr_token {
|
||||
proc_macro2::TokenTree::Group(group) => group.stream().to_string().contains("named_arg"),
|
||||
_ => abort_call_site!("Only option `TokenTree::Group` is needed")
|
||||
}
|
||||
})
|
||||
.map(|_| {
|
||||
let ty = &field.ty;
|
||||
let type_string = match ty {
|
||||
syn::Type::Path(type_path) => {
|
||||
match type_path.path.segments.last() {
|
||||
Some(path_segment) => path_segment.ident.to_string(),
|
||||
_ => String::new()
|
||||
}
|
||||
},
|
||||
_ => String::new()
|
||||
};
|
||||
let enum_for_clap_named_arg = syn::Ident::new(&format!("ClapNamedArg{}For{}", &type_string, &name), Span::call_site());
|
||||
quote! {
|
||||
#[derive(Debug, Clone, clap::Parser, interactive_clap_derive::ToCliArgs)]
|
||||
pub enum #enum_for_clap_named_arg {
|
||||
#(#attr_doc_vec)*
|
||||
#variant_name(<#ty as interactive_clap::ToCli>::CliVariant)
|
||||
}
|
||||
|
||||
impl From<#ty> for #enum_for_clap_named_arg {
|
||||
fn from(item: #ty) -> Self {
|
||||
Self::#variant_name(<#ty as interactive_clap::ToCli>::CliVariant::from(item))
|
||||
}
|
||||
impl From<#ty> for #enum_for_clap_named_arg {
|
||||
fn from(item: #ty) -> Self {
|
||||
Self::#variant_name(<#ty as interactive_clap::ToCli>::CliVariant::from(item))
|
||||
}
|
||||
}
|
||||
})
|
||||
.next()
|
||||
}) {
|
||||
token_stream
|
||||
} else {
|
||||
quote! ()
|
||||
};
|
||||
}
|
||||
})
|
||||
.next()
|
||||
})
|
||||
.unwrap_or(quote!());
|
||||
|
||||
quote! {
|
||||
#[derive(Debug, Default, Clone, clap::Parser, interactive_clap::ToCliArgs)]
|
||||
@@ -222,7 +218,6 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream {
|
||||
for attr in &variant.attrs {
|
||||
if attr.path.is_ident("doc") {
|
||||
attrs.push(attr.into_token_stream());
|
||||
// break;
|
||||
};
|
||||
if attr.path.is_ident("cfg") {
|
||||
for attr_token in attr.tokens.clone() {
|
||||
@@ -368,7 +363,9 @@ fn context_scope_for_struct(
|
||||
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 self::methods::fields_without_subcommand::is_field_without_subcommand(field) {
|
||||
if !self::methods::fields_with_subcommand::is_field_with_subcommand(field)
|
||||
&& !self::methods::fields_with_flatten::is_field_with_flatten(field)
|
||||
{
|
||||
quote! {
|
||||
pub #ident_field: #ty
|
||||
}
|
||||
|
||||
7
interactive-clap-derive/src/derives/to_cli_args/methods/interactive_clap_attrs_cli_field.rs
Normal file → Executable file
7
interactive-clap-derive/src/derives/to_cli_args/methods/interactive_clap_attrs_cli_field.rs
Normal file → Executable file
@@ -42,6 +42,13 @@ impl InteractiveClapAttrsCliField {
|
||||
.unwrap_or_default();
|
||||
});
|
||||
}
|
||||
if ident == "flatten" {
|
||||
args_without_attrs = quote! {
|
||||
if let Some(arg) = &self.#ident_field {
|
||||
args.append(&mut arg.to_cli_args())
|
||||
}
|
||||
};
|
||||
}
|
||||
if ident == "value_enum" {
|
||||
args_without_attrs = quote! {
|
||||
if let Some(arg) = &self.#ident_field {
|
||||
|
||||
Reference in New Issue
Block a user