diff --git a/interactive_clap_derive/src/derives/interactive_clap/methods/choose_variant.rs b/interactive_clap_derive/src/derives/interactive_clap/methods/choose_variant.rs index 9250eb2..8000ea9 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/methods/choose_variant.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/choose_variant.rs @@ -2,18 +2,22 @@ extern crate proc_macro; use proc_macro2::Span; use proc_macro_error::abort_call_site; -use syn; use quote::quote; +use syn; - -pub fn fn_choose_variant(ast: &syn::DeriveInput, variants: &syn::punctuated::Punctuated) -> proc_macro2::TokenStream { +pub fn fn_choose_variant( + ast: &syn::DeriveInput, + variants: &syn::punctuated::Punctuated, +) -> proc_macro2::TokenStream { let name = &ast.ident; - let interactive_clap_attrs_context = super::interactive_clap_attrs_context::InteractiveClapAttrsContext::new(&ast); - let command_discriminants = syn::Ident::new(&format!("{}Discriminants", name), Span::call_site()); + let interactive_clap_attrs_context = + super::interactive_clap_attrs_context::InteractiveClapAttrsContext::new(&ast); + let command_discriminants = + syn::Ident::new(&format!("{}Discriminants", name), Span::call_site()); let cli_command = syn::Ident::new(&format!("Cli{}", name), Span::call_site()); let variant_ident = &variants[0].ident; - let mut cli_variant = quote! (); + let mut cli_variant = quote!(); if !ast.attrs.is_empty() { for attr in ast.attrs.clone() { @@ -21,23 +25,30 @@ pub fn fn_choose_variant(ast: &syn::DeriveInput, variants: &syn::punctuated::Pun for attr_token in attr.tokens.clone() { match attr_token { proc_macro2::TokenTree::Group(group) => { - if group.stream().to_string().contains("disable_strum_discriminants").clone() { + if group + .stream() + .to_string() + .contains("disable_strum_discriminants") + .clone() + { match &variants[0].fields { syn::Fields::Unnamed(_) => { cli_variant = quote! { let cli_variant = #cli_command::#variant_ident(Default::default()); }; - }, + } syn::Fields::Unit => { cli_variant = quote! { let cli_variant = #cli_command::#variant_ident; }; - }, - _ => abort_call_site!("Only option `Fields::Unnamed` or `Fields::Unit` is needed") + } + _ => abort_call_site!( + "Only option `Fields::Unnamed` or `Fields::Unit` is needed" + ), } }; } - _ => () //abort_call_site!("Only option `TokenTree::Group` is needed") + _ => (), //abort_call_site!("Only option `TokenTree::Group` is needed") } } }; @@ -46,23 +57,29 @@ pub fn fn_choose_variant(ast: &syn::DeriveInput, variants: &syn::punctuated::Pun match attr_token { proc_macro2::TokenTree::Group(group) => { if &group.stream().to_string() == "derive(EnumMessage, EnumIter)" { - let doc_attrs = ast.attrs.iter() + let doc_attrs = ast + .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") + 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()); + let literal_vec = doc_attrs + .iter() + .map(|s| s.replace("\"", "")) + .collect::>(); + let literal = + proc_macro2::Literal::string(literal_vec.join("\n ").as_str()); let enum_variants = variants.iter().map(|variant| { let variant_ident = &variant.ident; @@ -80,8 +97,6 @@ pub fn fn_choose_variant(ast: &syn::DeriveInput, variants: &syn::punctuated::Pun }, _ => abort_call_site!("Only option `Fields::Unnamed` or `Fields::Unit` is needed") } - - }); cli_variant = quote! { @@ -101,27 +116,27 @@ pub fn fn_choose_variant(ast: &syn::DeriveInput, variants: &syn::punctuated::Pun .to_owned() }) .collect::>(); - + let selected = Select::with_theme(&ColorfulTheme::default()) .with_prompt(prompt) .items(&actions) .default(0) .interact() .unwrap(); - + variants[selected] } let cli_variant = match prompt_variant(#literal.to_string().as_str()) { #( #enum_variants, )* - }; + }; }; }; } - _ => () //abort_call_site!("Only option `TokenTree::Group` is needed") + _ => (), //abort_call_site!("Only option `TokenTree::Group` is needed") } } }; - }; + } }; let input_context = interactive_clap_attrs_context.get_input_context_dir(); diff --git a/interactive_clap_derive/src/derives/interactive_clap/methods/cli_field_type.rs b/interactive_clap_derive/src/derives/interactive_clap/methods/cli_field_type.rs index 908342b..97d7741 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/methods/cli_field_type.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/cli_field_type.rs @@ -1,38 +1,35 @@ extern crate proc_macro; use proc_macro_error::abort_call_site; -use syn; use quote::quote; - +use syn; pub fn cli_field_type(ty: &syn::Type) -> proc_macro2::TokenStream { match &ty { - syn::Type::Path(type_path) => { - match type_path.path.segments.first() { - Some(path_segment) => { - if path_segment.ident.eq("Option".into()) { - match &path_segment.arguments { - syn::PathArguments::AngleBracketed(gen_args) => { - let ty_option = &gen_args.args; - quote! { - Option<<#ty_option as interactive_clap::ToCli>::CliVariant> - } - }, - _ => { - quote! { - Option<<#ty as interactive_clap::ToCli>::CliVariant> - } - }, + syn::Type::Path(type_path) => match type_path.path.segments.first() { + Some(path_segment) => { + if path_segment.ident.eq("Option".into()) { + match &path_segment.arguments { + syn::PathArguments::AngleBracketed(gen_args) => { + let ty_option = &gen_args.args; + quote! { + Option<<#ty_option as interactive_clap::ToCli>::CliVariant> + } } - } else { - quote! { - Option<<#ty as interactive_clap::ToCli>::CliVariant> + _ => { + quote! { + Option<<#ty as interactive_clap::ToCli>::CliVariant> + } } } - }, - _ => abort_call_site!("Only option `PathSegment` is needed") + } else { + quote! { + Option<<#ty as interactive_clap::ToCli>::CliVariant> + } + } } + _ => abort_call_site!("Only option `PathSegment` is needed"), }, - _ => abort_call_site!("Only option `Type::Path` is needed") + _ => abort_call_site!("Only option `Type::Path` is needed"), } } 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 d4fdedb..415e994 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 @@ -21,9 +21,9 @@ pub fn is_field_without_skip_default_from_cli(field: &syn::Field) -> bool { } else { false } - }, - _ => false // abort_call_site!("Only option `TokenTree::Group` is needed") - }) + } + _ => false, // abort_call_site!("Only option `TokenTree::Group` is needed") + }) .next() { Some(token_stream) => false, 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 4d3904f..0d8c465 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 @@ -1,32 +1,35 @@ extern crate proc_macro; use proc_macro_error::abort_call_site; -use syn; use quote::quote; +use syn; pub fn is_field_without_subcommand(field: &syn::Field) -> bool { if field.attrs.is_empty() { - return true + return true; } - match field.attrs.iter() - .map(|attr| { - attr.tokens.clone() - }) + match field + .attrs + .iter() + .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") { + if group.stream().to_string().contains("named_arg") + || group.stream().to_string().contains("subcommand") + { true } else { false } - }, - _ => false // abort_call_site!("Only option `TokenTree::Group` is needed") + } + _ => false, // abort_call_site!("Only option `TokenTree::Group` is needed") } }) - .next() { - Some(token_stream) => false, - None => true - } + .next() + { + Some(token_stream) => false, + None => true, + } } diff --git a/interactive_clap_derive/src/derives/interactive_clap/methods/from_cli_for_enum.rs b/interactive_clap_derive/src/derives/interactive_clap/methods/from_cli_for_enum.rs index 7f2302d..c8c1c87 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/methods/from_cli_for_enum.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/from_cli_for_enum.rs @@ -2,17 +2,20 @@ extern crate proc_macro; use proc_macro2::Span; use proc_macro_error::abort_call_site; -use syn; use quote::quote; +use syn; - -pub fn from_cli_for_enum(ast: &syn::DeriveInput, variants: &syn::punctuated::Punctuated) -> proc_macro2::TokenStream { +pub fn from_cli_for_enum( + ast: &syn::DeriveInput, + variants: &syn::punctuated::Punctuated, +) -> proc_macro2::TokenStream { let name = &ast.ident; let cli_name = syn::Ident::new(&format!("Cli{}", name), Span::call_site()); - let interactive_clap_attrs_context = super::interactive_clap_attrs_context::InteractiveClapAttrsContext::new(&ast); + let interactive_clap_attrs_context = + super::interactive_clap_attrs_context::InteractiveClapAttrsContext::new(&ast); if interactive_clap_attrs_context.is_skip_default_from_cli { - return quote! (); + return quote!(); }; let from_cli_variants = variants.iter().map(|variant| { @@ -44,11 +47,12 @@ pub fn from_cli_for_enum(ast: &syn::DeriveInput, variants: &syn::punctuated::Pun }, _ => abort_call_site!("Only option `Fields::Unnamed` or `Fields::Unit` is needed") } - }); - let input_context_dir = interactive_clap_attrs_context.clone().get_input_context_dir(); - + let input_context_dir = interactive_clap_attrs_context + .clone() + .get_input_context_dir(); + quote! { pub fn from_cli( optional_clap_variant: Option<#cli_name>, @@ -56,7 +60,7 @@ pub fn from_cli_for_enum(ast: &syn::DeriveInput, variants: &syn::punctuated::Pun ) -> color_eyre::eyre::Result { match optional_clap_variant { #(#from_cli_variants)* - None => Self::choose_variant(context.clone()), + None => Self::choose_variant(context.clone()), } } } 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 2bbd073..4315c9b 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 @@ -2,69 +2,84 @@ extern crate proc_macro; use proc_macro2::Span; use proc_macro_error::abort_call_site; -use syn; use quote::quote; +use syn; - -pub fn from_cli_for_struct(ast: &syn::DeriveInput, fields: &syn::Fields) -> proc_macro2::TokenStream { +pub fn from_cli_for_struct( + ast: &syn::DeriveInput, + fields: &syn::Fields, +) -> proc_macro2::TokenStream { let name = &ast.ident; let cli_name = syn::Ident::new(&format!("Cli{}", name), Span::call_site()); - let interactive_clap_attrs_context = super::interactive_clap_attrs_context::InteractiveClapAttrsContext::new(&ast); + let interactive_clap_attrs_context = + super::interactive_clap_attrs_context::InteractiveClapAttrsContext::new(&ast); if interactive_clap_attrs_context.is_skip_default_from_cli { - return quote! (); + return quote!(); }; - let fields_without_subcommand = fields.iter() - .filter(|field| { - super::fields_without_subcommand::is_field_without_subcommand(field) - }) + let fields_without_subcommand = fields + .iter() + .filter(|field| super::fields_without_subcommand::is_field_without_subcommand(field)) .map(|field| { let ident_field = &field.clone().ident.expect("this field does not exist"); quote! {#ident_field} }) .collect::>(); - let fields_value = fields.iter().map(|field| { - fields_value(field) - }) - .filter(|token_stream| !token_stream.is_empty()); + let fields_value = fields + .iter() + .map(|field| fields_value(field)) + .filter(|token_stream| !token_stream.is_empty()); - let field_value_named_arg = - if let Some(token_stream) = fields.iter().map(|field| { - field_value_named_arg(name, field, &interactive_clap_attrs_context.output_context_dir) + let field_value_named_arg = if let Some(token_stream) = fields + .iter() + .map(|field| { + field_value_named_arg( + name, + field, + &interactive_clap_attrs_context.output_context_dir, + ) }) .filter(|token_stream| !token_stream.is_empty()) .next() - { - token_stream - } else { - quote! () - }; + { + token_stream + } else { + quote!() + }; - let field_value_subcommand = - if let Some(token_stream) = fields.iter().map(|field| { - field_value_subcommand(name, field, &interactive_clap_attrs_context.output_context_dir) + let field_value_subcommand = if let Some(token_stream) = fields + .iter() + .map(|field| { + field_value_subcommand( + name, + field, + &interactive_clap_attrs_context.output_context_dir, + ) }) .filter(|token_stream| !token_stream.is_empty()) .next() - { - token_stream - } else { - quote! () - }; + { + token_stream + } else { + quote!() + }; - let struct_fields = fields.iter().map(|field| { - struct_field(field, &fields_without_subcommand) - }); + let struct_fields = fields + .iter() + .map(|field| struct_field(field, &fields_without_subcommand)); let input_context_dir = interactive_clap_attrs_context.get_input_context_dir(); - let interactive_clap_context_scope_for_struct = syn::Ident::new(&format!("InteractiveClapContextScopeFor{}", &name), Span::call_site()); + let interactive_clap_context_scope_for_struct = syn::Ident::new( + &format!("InteractiveClapContextScopeFor{}", &name), + Span::call_site(), + ); let new_context_scope = quote! { let new_context_scope = #interactive_clap_context_scope_for_struct { #(#fields_without_subcommand,)* }; }; - + quote! { pub fn from_cli( optional_clap_variant: Option<#cli_name>, @@ -92,15 +107,19 @@ fn fields_value(field: &syn::Field) -> proc_macro2::TokenStream { )?; } } else { - quote! () + quote!() } } -fn field_value_named_arg(name: &syn::Ident, field: &syn::Field, output_context_dir: &Option) -> proc_macro2::TokenStream { +fn field_value_named_arg( + name: &syn::Ident, + field: &syn::Field, + output_context_dir: &Option, +) -> 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! () + quote!() } else { match field.attrs.iter() .filter(|attr| attr.path.is_ident("interactive_clap".into())) @@ -163,11 +182,15 @@ fn field_value_named_arg(name: &syn::Ident, field: &syn::Field, output_context_d } } -fn field_value_subcommand(name: &syn::Ident, field: &syn::Field, output_context_dir: &Option) -> proc_macro2::TokenStream { +fn field_value_subcommand( + name: &syn::Ident, + field: &syn::Field, + output_context_dir: &Option, +) -> 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! () + quote!() } else { match field.attrs.iter() .filter(|attr| attr.path.is_ident("interactive_clap".into())) @@ -212,9 +235,15 @@ fn field_value_subcommand(name: &syn::Ident, field: &syn::Field, output_context_ } } -fn struct_field(field: &syn::Field, fields_without_subcommand: &Vec) -> proc_macro2::TokenStream { +fn struct_field( + field: &syn::Field, + fields_without_subcommand: &Vec, +) -> proc_macro2::TokenStream { let ident_field = &field.clone().ident.expect("this field does not exist"); - let fields_without_subcommand_to_string = fields_without_subcommand.iter().map(|token_stream| token_stream.to_string()).collect::>(); + let fields_without_subcommand_to_string = fields_without_subcommand + .iter() + .map(|token_stream| token_stream.to_string()) + .collect::>(); if fields_without_subcommand_to_string.contains(&ident_field.to_string()) { quote! { #ident_field: new_context_scope.#ident_field diff --git a/interactive_clap_derive/src/derives/interactive_clap/methods/get_arg_from_cli_for_struct.rs b/interactive_clap_derive/src/derives/interactive_clap/methods/get_arg_from_cli_for_struct.rs index ab1081c..cef39dd 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/methods/get_arg_from_cli_for_struct.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/get_arg_from_cli_for_struct.rs @@ -1,19 +1,31 @@ extern crate proc_macro; use proc_macro2::Span; -use syn; use quote::quote; - +use syn; pub fn from_cli_arg(ast: &syn::DeriveInput, fields: &syn::Fields) -> Vec { - let interactive_clap_attrs_context = super::interactive_clap_attrs_context::InteractiveClapAttrsContext::new(&ast); + 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! ()]; + return vec![quote!()]; }; - let fields_without_subcommand = fields.iter() + let fields_without_subcommand = fields + .iter() + .filter(|field| super::fields_without_subcommand::is_field_without_subcommand(field)) + .map(|field| { + let ident_field = &field.clone().ident.expect("this field does not exist"); + quote! {#ident_field} + }) + .collect::>(); + + let fields_without_skip_default_from_cli = fields + .iter() .filter(|field| { - super::fields_without_subcommand::is_field_without_subcommand(field) + super::fields_without_skip_default_from_cli::is_field_without_skip_default_from_cli( + field, + ) }) .map(|field| { let ident_field = &field.clone().ident.expect("this field does not exist"); @@ -21,44 +33,50 @@ pub fn from_cli_arg(ast: &syn::DeriveInput, fields: &syn::Fields) -> Vec>(); - let fields_without_skip_default_from_cli = fields.iter() - .filter(|field| { - super::fields_without_skip_default_from_cli::is_field_without_skip_default_from_cli(field) - }) + let get_arg_for_fields = fields + .iter() .map(|field| { let ident_field = &field.clone().ident.expect("this field does not exist"); - quote! {#ident_field} - }) - .collect::>(); - - let get_arg_for_fields = fields.iter().map(|field| { - let ident_field = &field.clone().ident.expect("this field does not exist"); - let ty = &field.ty; - let fields_without_subcommand_to_string = fields_without_subcommand.iter().map(|token_stream| token_stream.to_string()).collect::>(); - let fields_without_skip_default_from_cli_to_string = fields_without_skip_default_from_cli.iter().map(|token_stream| token_stream.to_string()).collect::>(); - if fields_without_subcommand_to_string.contains(&ident_field.to_string()) & fields_without_skip_default_from_cli_to_string.contains(&ident_field.to_string()) { - let fn_from_cli_arg = syn::Ident::new(&format!("from_cli_{}", &ident_field), Span::call_site()); - let optional_cli_field_name = syn::Ident::new(&format!("optional_cli_{}", ident_field), Span::call_site()); - let input_context_dir = interactive_clap_attrs_context.clone().get_input_context_dir(); - let cli_field_type = super::cli_field_type::cli_field_type(ty); - let fn_input_arg = syn::Ident::new(&format!("input_{}", &ident_field), Span::call_site()); - quote! { - fn #fn_from_cli_arg( - #optional_cli_field_name: #cli_field_type, - context: &#input_context_dir, - ) -> color_eyre::eyre::Result<#ty> { - match #optional_cli_field_name { - Some(#ident_field) => Ok(#ident_field), - None => Self::#fn_input_arg(&context), + let ty = &field.ty; + let fields_without_subcommand_to_string = fields_without_subcommand + .iter() + .map(|token_stream| token_stream.to_string()) + .collect::>(); + let fields_without_skip_default_from_cli_to_string = + fields_without_skip_default_from_cli + .iter() + .map(|token_stream| token_stream.to_string()) + .collect::>(); + if fields_without_subcommand_to_string.contains(&ident_field.to_string()) + & fields_without_skip_default_from_cli_to_string.contains(&ident_field.to_string()) + { + let fn_from_cli_arg = + syn::Ident::new(&format!("from_cli_{}", &ident_field), Span::call_site()); + let optional_cli_field_name = + syn::Ident::new(&format!("optional_cli_{}", ident_field), Span::call_site()); + let input_context_dir = interactive_clap_attrs_context + .clone() + .get_input_context_dir(); + let cli_field_type = super::cli_field_type::cli_field_type(ty); + let fn_input_arg = + syn::Ident::new(&format!("input_{}", &ident_field), Span::call_site()); + quote! { + fn #fn_from_cli_arg( + #optional_cli_field_name: #cli_field_type, + context: &#input_context_dir, + ) -> color_eyre::eyre::Result<#ty> { + match #optional_cli_field_name { + Some(#ident_field) => Ok(#ident_field), + None => Self::#fn_input_arg(&context), + } } } + } else { + quote!() } - } else { - quote! () - } - }) - .filter(|token_stream| !token_stream.is_empty()) - .collect::>(); + }) + .filter(|token_stream| !token_stream.is_empty()) + .collect::>(); get_arg_for_fields } diff --git a/interactive_clap_derive/src/derives/interactive_clap/methods/interactive_clap_attrs_context.rs b/interactive_clap_derive/src/derives/interactive_clap/methods/interactive_clap_attrs_context.rs index 57f7492..df284b5 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/methods/interactive_clap_attrs_context.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/interactive_clap_attrs_context.rs @@ -1,7 +1,7 @@ extern crate proc_macro; -use syn; use quote::quote; +use syn; #[derive(Debug, Clone)] pub struct InteractiveClapAttrsContext { @@ -12,12 +12,10 @@ pub struct InteractiveClapAttrsContext { } impl InteractiveClapAttrsContext { - pub fn new( - ast: &syn::DeriveInput, - ) -> Self { - let mut context_dir = quote! (); - let mut input_context_dir = quote! (); - let mut output_context_dir = quote! (); + pub fn new(ast: &syn::DeriveInput) -> Self { + let mut context_dir = quote!(); + let mut input_context_dir = quote!(); + let mut output_context_dir = quote!(); let mut is_skip_default_from_cli = false; if !ast.attrs.is_empty() { for attr in ast.attrs.clone() { @@ -26,46 +24,45 @@ impl InteractiveClapAttrsContext { match attr_token { proc_macro2::TokenTree::Group(group) => { if group.stream().to_string().contains("output_context") { - let group_stream = &group.stream() - .into_iter() - .collect::>()[2..]; + let group_stream = + &group.stream().into_iter().collect::>()[2..]; output_context_dir = quote! {#(#group_stream)*}; } else if group.stream().to_string().contains("input_context") { - let group_stream = &group.stream() - .into_iter() - .collect::>()[2..]; + let group_stream = + &group.stream().into_iter().collect::>()[2..]; input_context_dir = quote! {#(#group_stream)*}; } else if group.stream().to_string().contains("context") { - let group_stream = &group.stream() - .into_iter() - .collect::>()[2..]; + let group_stream = + &group.stream().into_iter().collect::>()[2..]; context_dir = quote! {#(#group_stream)*}; }; if group.stream().to_string().contains("skip_default_from_cli") { is_skip_default_from_cli = true; }; } - _ => () //abort_call_site!("Only option `TokenTree::Group` is needed") + _ => (), //abort_call_site!("Only option `TokenTree::Group` is needed") } } }; - }; + } }; let context_dir: Option = if let true = !context_dir.is_empty() { Some(context_dir) } else { None }; - let input_context_dir: Option = if let true = !input_context_dir.is_empty() { - Some(input_context_dir) - } else { - None - }; - let output_context_dir: Option = if let true = !output_context_dir.is_empty() { - Some(output_context_dir) - } else { - None - }; + let input_context_dir: Option = + if let true = !input_context_dir.is_empty() { + Some(input_context_dir) + } else { + None + }; + let output_context_dir: Option = + if let true = !output_context_dir.is_empty() { + Some(output_context_dir) + } else { + None + }; Self { context_dir, input_context_dir, @@ -77,12 +74,14 @@ impl InteractiveClapAttrsContext { pub fn get_input_context_dir(self) -> proc_macro2::TokenStream { let context_dir = match self.context_dir { Some(context_dir) => context_dir, - None => quote! () + None => quote!(), + }; + if !context_dir.is_empty() { + return context_dir; }; - if !context_dir.is_empty() { return context_dir }; let input_context_dir = match self.input_context_dir { Some(input_context_dir) => input_context_dir, - None => quote! {()} + None => quote! {()}, }; input_context_dir } diff --git a/interactive_clap_derive/src/derives/interactive_clap/mod.rs b/interactive_clap_derive/src/derives/interactive_clap/mod.rs index cf049f2..6d424b2 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/mod.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/mod.rs @@ -3,12 +3,11 @@ extern crate proc_macro; use proc_macro::TokenStream; use proc_macro2::Span; use proc_macro_error::abort_call_site; +use quote::{quote, ToTokens}; use syn; -use quote::{ToTokens, quote}; mod methods; - pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let cli_name_string = format!("Cli{}", &ast.ident); @@ -18,92 +17,112 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream { let fields = data_struct.fields.clone(); let mut ident_skip_field_vec: Vec = Vec::new(); - let cli_fields = fields.iter().map(|field| { - let ident_field = field.ident.clone().expect("this field does not exist"); - let ty = &field.ty; - let cli_ty = self::methods::cli_field_type::cli_field_type(ty); - let mut cli_field = quote! { - pub #ident_field: #cli_ty - }; - if field.attrs.is_empty() { - return cli_field - }; - let mut clap_attr_vec: Vec = Vec::new(); - let mut cfg_attr_vec: Vec = Vec::new(); - for attr in &field.attrs { - if attr.path.is_ident("interactive_clap".into()) | attr.path.is_ident("cfg".into()) { - for attr_token in attr.tokens.clone() { - match attr_token { - proc_macro2::TokenTree::Group(group) => { - if group.stream().to_string().contains("subcommand") | group.stream().to_string().contains("long") | (group.stream().to_string() == "skip".to_string()) { - clap_attr_vec.push(group.stream()) - } else if group.stream().to_string().contains("named_arg") { - let ident_subcommand = syn::Ident::new("subcommand", Span::call_site()); - clap_attr_vec.push(quote! {#ident_subcommand}); - 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() + let cli_fields = fields + .iter() + .map(|field| { + let ident_field = field.ident.clone().expect("this field does not exist"); + let ty = &field.ty; + let cli_ty = self::methods::cli_field_type::cli_field_type(ty); + let mut cli_field = quote! { + pub #ident_field: #cli_ty + }; + if field.attrs.is_empty() { + return cli_field; + }; + let mut clap_attr_vec: Vec = Vec::new(); + let mut cfg_attr_vec: Vec = Vec::new(); + for attr in &field.attrs { + if attr.path.is_ident("interactive_clap".into()) + | attr.path.is_ident("cfg".into()) + { + for attr_token in attr.tokens.clone() { + match attr_token { + proc_macro2::TokenTree::Group(group) => { + if group.stream().to_string().contains("subcommand") + | group.stream().to_string().contains("long") + | (group.stream().to_string() == "skip".to_string()) + { + clap_attr_vec.push(group.stream()) + } else if group.stream().to_string().contains("named_arg") { + let ident_subcommand = + syn::Ident::new("subcommand", Span::call_site()); + clap_attr_vec.push(quote! {#ident_subcommand}); + 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() + _ => String::new(), + }; + let enum_for_clap_named_arg = syn::Ident::new( + &format!( + "ClapNamedArg{}For{}", + &type_string, &name + ), + Span::call_site(), + ); + cli_field = quote! { + pub #ident_field: Option<#enum_for_clap_named_arg> + } }; - let enum_for_clap_named_arg = syn::Ident::new(&format!("ClapNamedArg{}For{}", &type_string, &name), Span::call_site()); - cli_field = quote! { - pub #ident_field: Option<#enum_for_clap_named_arg> - } - }; - if group.stream().to_string().contains("feature") { - cfg_attr_vec.push(attr.into_token_stream()) - }; - if group.stream().to_string() == "skip".to_string() { - ident_skip_field_vec.push(ident_field.clone()); - cli_field = quote! () - }; - }, - _ => abort_call_site!("Only option `TokenTree::Group` is needed") + if group.stream().to_string().contains("feature") { + cfg_attr_vec.push(attr.into_token_stream()) + }; + if group.stream().to_string() == "skip".to_string() { + ident_skip_field_vec.push(ident_field.clone()); + cli_field = quote!() + }; + } + _ => { + abort_call_site!("Only option `TokenTree::Group` is needed") + } + } } - }; + } } - }; - if cli_field.is_empty() { - return cli_field - }; - let cfg_attrs = cfg_attr_vec.iter(); - if !clap_attr_vec.is_empty() { - let clap_attrs = clap_attr_vec.iter(); - quote! { - #(#cfg_attrs)* - #[clap(#(#clap_attrs, )*)] - #cli_field + if cli_field.is_empty() { + return cli_field; + }; + let cfg_attrs = cfg_attr_vec.iter(); + if !clap_attr_vec.is_empty() { + let clap_attrs = clap_attr_vec.iter(); + quote! { + #(#cfg_attrs)* + #[clap(#(#clap_attrs, )*)] + #cli_field + } + } else { + quote! { + #(#cfg_attrs)* + #cli_field + } } - } else { - quote! { - #(#cfg_attrs)* - #cli_field - } - } - }) - .filter(|token_stream| !token_stream.is_empty()) - .collect::>(); - - let for_cli_fields = fields.iter().map(|field| { - for_cli_field(field, &ident_skip_field_vec) - }) - .filter(|token_stream| !token_stream.is_empty()); + }) + .filter(|token_stream| !token_stream.is_empty()) + .collect::>(); - let fn_from_cli_for_struct = self::methods::from_cli_for_struct::from_cli_for_struct(&ast, &fields); + let for_cli_fields = fields + .iter() + .map(|field| for_cli_field(field, &ident_skip_field_vec)) + .filter(|token_stream| !token_stream.is_empty()); - let fn_get_arg = self::methods::get_arg_from_cli_for_struct::from_cli_arg(&ast, &fields); + let fn_from_cli_for_struct = + self::methods::from_cli_for_struct::from_cli_for_struct(&ast, &fields); + + 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) - }) - .filter(|token_stream| !token_stream.is_empty()) - .collect::>(); + let context_scope_fields = fields + .iter() + .map(|field| context_scope_for_struct_field(field)) + .filter(|token_stream| !token_stream.is_empty()) + .collect::>(); let context_scope_for_struct = context_scope_for_struct(&name, context_scope_fields); let clap_enum_for_named_arg = @@ -115,7 +134,6 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream { .filter(|attr| attr.path.is_ident("doc".into())) .map(|attr| attr.into_token_stream()) .collect(); - field.attrs.iter() .filter(|attr| attr.path.is_ident("interactive_clap".into())) .map(|attr| attr.tokens.clone()) @@ -224,16 +242,17 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream { proc_macro2::TokenTree::Group(group) => { if group.stream().to_string().contains("feature") { attrs.push(attr.into_token_stream()); - } else { continue; }; - }, - _ => abort_call_site!("Only option `TokenTree::Group` is needed") + } + _ => { + abort_call_site!("Only option `TokenTree::Group` is needed") + } } - }; + } }; - }; + } match &variant.fields { syn::Fields::Unnamed(fields) => { let ty = &fields.unnamed[0].ty; @@ -245,7 +264,7 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream { #ident(<#ty as interactive_clap::ToCli>::CliVariant) } } - }, + } syn::Fields::Unit => { if attrs.is_empty() { quote! {#ident} @@ -255,20 +274,23 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream { #ident } } - }, - _ => abort_call_site!("Only option `Fields::Unnamed` or `Fields::Unit` is needed") + } + _ => abort_call_site!( + "Only option `Fields::Unnamed` or `Fields::Unit` is needed" + ), } } else { match &variant.fields { syn::Fields::Unnamed(fields) => { let ty = &fields.unnamed[0].ty; quote! { #ident(<#ty as interactive_clap::ToCli>::CliVariant) } - - }, + } syn::Fields::Unit => { quote! { #ident } - }, - _ => abort_call_site!("Only option `Fields::Unnamed` or `Fields::Unit` is needed") + } + _ => abort_call_site!( + "Only option `Fields::Unnamed` or `Fields::Unit` is needed" + ), } } }); @@ -277,11 +299,13 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream { match &variant.fields { syn::Fields::Unnamed(_) => { quote! { #name::#ident(arg) => Self::#ident(arg.into()) } - }, + } syn::Fields::Unit => { quote! { #name::#ident => Self::#ident } - }, - _ => abort_call_site!("Only option `Fields::Unnamed` or `Fields::Unit` is needed") + } + _ => abort_call_site!( + "Only option `Fields::Unnamed` or `Fields::Unit` is needed" + ), } }); @@ -289,7 +313,8 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream { let fn_choose_variant = self::methods::choose_variant::fn_choose_variant(ast, variants); - let fn_from_cli_for_enum = self::methods::from_cli_for_enum::from_cli_for_enum(ast, variants); + let fn_from_cli_for_enum = + self::methods::from_cli_for_enum::from_cli_for_enum(ast, variants); let gen = quote! { #[derive(Debug, Clone, clap::Clap, interactive_clap_derive::ToCliArgs)] @@ -302,7 +327,7 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream { } #scope_for_enum - + impl From<#name> for #cli_name { fn from(command: #name) -> Self { match command { @@ -310,7 +335,7 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream { } } } - + impl #name { #fn_choose_variant #fn_from_cli_for_enum @@ -318,12 +343,18 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream { }; gen.into() } - _ => abort_call_site!("`#[derive(InteractiveClap)]` only supports structs and enums") + _ => abort_call_site!("`#[derive(InteractiveClap)]` only supports structs and enums"), } } -fn context_scope_for_struct(name: &syn::Ident, context_scope_fields: Vec) -> proc_macro2::TokenStream { - let interactive_clap_context_scope_for_struct = syn::Ident::new(&format!("InteractiveClapContextScopeFor{}", &name), Span::call_site()); +fn context_scope_for_struct( + name: &syn::Ident, + context_scope_fields: Vec, +) -> proc_macro2::TokenStream { + let interactive_clap_context_scope_for_struct = syn::Ident::new( + &format!("InteractiveClapContextScopeFor{}", &name), + Span::call_site(), + ); quote! { pub struct #interactive_clap_context_scope_for_struct { #(#context_scope_fields,)* @@ -342,13 +373,15 @@ fn context_scope_for_struct_field(field: &syn::Field) -> proc_macro2::TokenStrea pub #ident_field: #ty } } else { - quote! () + quote!() } - } fn context_scope_for_enum(name: &syn::Ident) -> proc_macro2::TokenStream { - let interactive_clap_context_scope_for_enum = syn::Ident::new(&format!("InteractiveClapContextScopeFor{}", &name), Span::call_site()); + let interactive_clap_context_scope_for_enum = syn::Ident::new( + &format!("InteractiveClapContextScopeFor{}", &name), + Span::call_site(), + ); let enum_discriminants = syn::Ident::new(&format!("{}Discriminants", &name), Span::call_site()); quote! { pub type #interactive_clap_context_scope_for_enum = #enum_discriminants; @@ -358,29 +391,31 @@ fn context_scope_for_enum(name: &syn::Ident) -> proc_macro2::TokenStream { } } -fn for_cli_field(field: &syn::Field, ident_skip_field_vec: &Vec) -> proc_macro2::TokenStream { +fn for_cli_field( + field: &syn::Field, + ident_skip_field_vec: &Vec, +) -> proc_macro2::TokenStream { let ident_field = &field.clone().ident.expect("this field does not exist"); if ident_skip_field_vec.contains(&ident_field) { - quote! () + quote!() } else { let ty = &field.ty; match &ty { - syn::Type::Path(type_path) => { - match type_path.path.segments.first() { - Some(path_segment) => { - if path_segment.ident.eq("Option".into()) { - quote! { - #ident_field: args.#ident_field.into() - } - } else { - quote! { - #ident_field: Some(args.#ident_field.into()) - } + syn::Type::Path(type_path) => match type_path.path.segments.first() { + Some(path_segment) => { + if path_segment.ident.eq("Option".into()) { + quote! { + #ident_field: args.#ident_field.into() } - }, - _ => abort_call_site!("Only option `PathSegment` is needed") - }}, - _ => abort_call_site!("Only option `Type::Path` is needed") + } else { + quote! { + #ident_field: Some(args.#ident_field.into()) + } + } + } + _ => abort_call_site!("Only option `PathSegment` is needed"), + }, + _ => abort_call_site!("Only option `Type::Path` is needed"), } } } diff --git a/interactive_clap_derive/src/derives/to_cli_args/methods/interactive_clap_attrs_cli_field.rs b/interactive_clap_derive/src/derives/to_cli_args/methods/interactive_clap_attrs_cli_field.rs index f5bafe2..c2b8650 100644 --- a/interactive_clap_derive/src/derives/to_cli_args/methods/interactive_clap_attrs_cli_field.rs +++ b/interactive_clap_derive/src/derives/to_cli_args/methods/interactive_clap_attrs_cli_field.rs @@ -2,9 +2,8 @@ extern crate proc_macro; use proc_macro2::Span; use proc_macro_error::abort_call_site; -use syn; use quote::quote; - +use syn; #[derive(Debug, Clone)] pub struct InteractiveClapAttrsCliField { @@ -21,12 +20,12 @@ impl InteractiveClapAttrsCliField { let mut subcommand_args = quote! { let mut args = std::collections::VecDeque::new(); }; - let mut args_without_attrs = quote! (); - let mut named_args = quote! (); - let mut unnamed_args = quote! (); - + let mut args_without_attrs = quote!(); + let mut named_args = quote!(); + let mut unnamed_args = quote!(); + if field.attrs.is_empty() { - args_without_attrs = quote!{ + args_without_attrs = quote! { if let Some(arg) = &self.#ident_field { args.push_front(arg.to_string()) } @@ -49,46 +48,52 @@ impl InteractiveClapAttrsCliField { .unwrap_or_default(); }; } else if "long".to_string() == ident.to_string() { - let ident_field_to_kebab_case_string = crate::helpers::to_kebab_case::to_kebab_case(ident_field.to_string()); - let ident_field_to_kebab_case = &syn::LitStr::new(&ident_field_to_kebab_case_string, Span::call_site()); - unnamed_args = quote!{ + let ident_field_to_kebab_case_string = + crate::helpers::to_kebab_case::to_kebab_case( + ident_field.to_string(), + ); + let ident_field_to_kebab_case = &syn::LitStr::new( + &ident_field_to_kebab_case_string, + Span::call_site(), + ); + unnamed_args = quote! { if let Some(arg) = &self.#ident_field { args.push_front(arg.to_string()); args.push_front(std::concat!("--", #ident_field_to_kebab_case).to_string()); } }; } - }, - proc_macro2::TokenTree::Literal(literal) =>{ - named_args = quote!{ - if let Some(arg) = &self.#ident_field { - args.push_front(arg.to_string()); - args.push_front(std::concat!("--", #literal).to_string()); - } - }; - }, - _ => () //abort_call_site!("Only option `TokenTree::Ident` is needed") + } + proc_macro2::TokenTree::Literal(literal) => { + named_args = quote! { + if let Some(arg) = &self.#ident_field { + args.push_front(arg.to_string()); + args.push_front(std::concat!("--", #literal).to_string()); + } + }; + } + _ => (), //abort_call_site!("Only option `TokenTree::Ident` is needed") }; - }; - }, - _ => abort_call_site!("Only option `TokenTree::Group` is needed") + } + } + _ => abort_call_site!("Only option `TokenTree::Group` is needed"), } - }; + } } - }; + } }; - Self{ + Self { ident_field, args_without_attrs: Some(args_without_attrs), named_args: Some(named_args.clone()), unnamed_args: { - if !named_args.is_empty() { - None - } else { - Some(unnamed_args) - } - }, - subcommand_args: Some(subcommand_args) + if !named_args.is_empty() { + None + } else { + Some(unnamed_args) + } + }, + subcommand_args: Some(subcommand_args), } } } diff --git a/interactive_clap_derive/src/derives/to_cli_args/mod.rs b/interactive_clap_derive/src/derives/to_cli_args/mod.rs index d9f2b25..9fd6010 100644 --- a/interactive_clap_derive/src/derives/to_cli_args/mod.rs +++ b/interactive_clap_derive/src/derives/to_cli_args/mod.rs @@ -3,12 +3,11 @@ extern crate proc_macro; use proc_macro::TokenStream; use proc_macro2::Span; use proc_macro_error::abort_call_site; -use syn; use quote::quote; +use syn; mod methods; - pub fn impl_to_cli_args(ast: &syn::DeriveInput) -> TokenStream { let cli_name = &ast.ident; match &ast.data { @@ -17,15 +16,19 @@ pub fn impl_to_cli_args(ast: &syn::DeriveInput) -> TokenStream { let mut args = std::collections::VecDeque::new(); }; let mut args_push_front_vec: Vec = Vec::new(); - + for field in data_struct.clone().fields.iter() { let interactive_clap_attrs_cli_field = self::methods::interactive_clap_attrs_cli_field::InteractiveClapAttrsCliField::new(field.clone()); - args_subcommand = if let Some(subcommand_args) = interactive_clap_attrs_cli_field.subcommand_args { + args_subcommand = if let Some(subcommand_args) = + interactive_clap_attrs_cli_field.subcommand_args + { subcommand_args } else { args_subcommand }; - if let Some(args_without_attrs) = interactive_clap_attrs_cli_field.args_without_attrs { + if let Some(args_without_attrs) = + interactive_clap_attrs_cli_field.args_without_attrs + { args_push_front_vec.push(args_without_attrs) }; if let Some(named_args) = interactive_clap_attrs_cli_field.named_args { @@ -34,7 +37,7 @@ pub fn impl_to_cli_args(ast: &syn::DeriveInput) -> TokenStream { if let Some(unnamed_args) = interactive_clap_attrs_cli_field.unnamed_args { args_push_front_vec.push(unnamed_args) }; - }; + } let args_push_front_vec = args_push_front_vec.into_iter().rev(); let gen = quote! { @@ -47,11 +50,12 @@ pub fn impl_to_cli_args(ast: &syn::DeriveInput) -> TokenStream { } }; gen.into() - }, + } syn::Data::Enum(syn::DataEnum { variants, .. }) => { let enum_variants = variants.iter().map(|variant| { let ident = &variant.ident; - let variant_name_string = crate::helpers::to_kebab_case::to_kebab_case(ident.to_string()); + let variant_name_string = + crate::helpers::to_kebab_case::to_kebab_case(ident.to_string()); let variant_name = &syn::LitStr::new(&variant_name_string, Span::call_site()); match &variant.fields { @@ -63,7 +67,7 @@ pub fn impl_to_cli_args(ast: &syn::DeriveInput) -> TokenStream { args } } - }, + } syn::Fields::Unit => { quote! { Self::#ident => { @@ -72,8 +76,10 @@ pub fn impl_to_cli_args(ast: &syn::DeriveInput) -> TokenStream { args } } - }, - _ => abort_call_site!("Only options `Fields::Unnamed` or `Fields::Unit` are needed") + } + _ => abort_call_site!( + "Only options `Fields::Unnamed` or `Fields::Unit` are needed" + ), } }); let gen = quote! { @@ -86,7 +92,7 @@ pub fn impl_to_cli_args(ast: &syn::DeriveInput) -> TokenStream { } }; gen.into() - }, - _ => abort_call_site!("`#[derive(InteractiveClap)]` only supports structs and enums") + } + _ => abort_call_site!("`#[derive(InteractiveClap)]` only supports structs and enums"), } } diff --git a/interactive_clap_derive/src/helpers/mod.rs b/interactive_clap_derive/src/helpers/mod.rs index 9fc2923..970a12f 100644 --- a/interactive_clap_derive/src/helpers/mod.rs +++ b/interactive_clap_derive/src/helpers/mod.rs @@ -1,2 +1,2 @@ -pub mod to_kebab_case; pub mod snake_case_to_camel_case; +pub mod to_kebab_case; diff --git a/interactive_clap_derive/src/helpers/snake_case_to_camel_case.rs b/interactive_clap_derive/src/helpers/snake_case_to_camel_case.rs index 1488977..696b67b 100644 --- a/interactive_clap_derive/src/helpers/snake_case_to_camel_case.rs +++ b/interactive_clap_derive/src/helpers/snake_case_to_camel_case.rs @@ -1,5 +1,7 @@ pub fn snake_case_to_camel_case(s: String) -> String { - let s_vec: Vec = s.to_lowercase().split("_") + let s_vec: Vec = s + .to_lowercase() + .split("_") .map(|s| s.replacen(&s[..1], &s[..1].to_ascii_uppercase(), 1)) .collect(); s_vec.join("") diff --git a/interactive_clap_derive/src/helpers/to_kebab_case.rs b/interactive_clap_derive/src/helpers/to_kebab_case.rs index 776b6b8..288e9df 100644 --- a/interactive_clap_derive/src/helpers/to_kebab_case.rs +++ b/interactive_clap_derive/src/helpers/to_kebab_case.rs @@ -5,6 +5,6 @@ pub fn to_kebab_case(s: String) -> String { snake.push('-'); } snake.push(ch.to_ascii_lowercase()); - }; + } snake.as_str().replace("_", "-") } diff --git a/interactive_clap_derive/src/lib.rs b/interactive_clap_derive/src/lib.rs index ed07335..d82631f 100644 --- a/interactive_clap_derive/src/lib.rs +++ b/interactive_clap_derive/src/lib.rs @@ -7,7 +7,6 @@ use syn; mod derives; mod helpers; - #[proc_macro_derive(InteractiveClap, attributes(interactive_clap))] #[proc_macro_error] pub fn interactive_clap(input: TokenStream) -> TokenStream {