mirror of
https://github.com/Drop-OSS/interactive-clap.git
synced 2026-01-30 20:55:25 +01:00
fmt
This commit is contained in:
@@ -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<syn::Variant, syn::token::Comma>) -> proc_macro2::TokenStream {
|
||||
pub fn fn_choose_variant(
|
||||
ast: &syn::DeriveInput,
|
||||
variants: &syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>,
|
||||
) -> 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::<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());
|
||||
let literal_vec = doc_attrs
|
||||
.iter()
|
||||
.map(|s| s.replace("\"", ""))
|
||||
.collect::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
|
||||
|
||||
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();
|
||||
|
||||
|
||||
@@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<syn::Variant, syn::token::Comma>) -> proc_macro2::TokenStream {
|
||||
pub fn from_cli_for_enum(
|
||||
ast: &syn::DeriveInput,
|
||||
variants: &syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>,
|
||||
) -> 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<Self> {
|
||||
match optional_clap_variant {
|
||||
#(#from_cli_variants)*
|
||||
None => Self::choose_variant(context.clone()),
|
||||
None => Self::choose_variant(context.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::<Vec<_>>();
|
||||
|
||||
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>) -> proc_macro2::TokenStream {
|
||||
fn field_value_named_arg(
|
||||
name: &syn::Ident,
|
||||
field: &syn::Field,
|
||||
output_context_dir: &Option<proc_macro2::TokenStream>,
|
||||
) -> 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>) -> proc_macro2::TokenStream {
|
||||
fn field_value_subcommand(
|
||||
name: &syn::Ident,
|
||||
field: &syn::Field,
|
||||
output_context_dir: &Option<proc_macro2::TokenStream>,
|
||||
) -> 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>) -> proc_macro2::TokenStream {
|
||||
fn struct_field(
|
||||
field: &syn::Field,
|
||||
fields_without_subcommand: &Vec<proc_macro2::TokenStream>,
|
||||
) -> 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::<Vec<_>>();
|
||||
let fields_without_subcommand_to_string = fields_without_subcommand
|
||||
.iter()
|
||||
.map(|token_stream| token_stream.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
if fields_without_subcommand_to_string.contains(&ident_field.to_string()) {
|
||||
quote! {
|
||||
#ident_field: new_context_scope.#ident_field
|
||||
|
||||
@@ -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<proc_macro2::TokenStream> {
|
||||
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::<Vec<_>>();
|
||||
|
||||
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<proc_ma
|
||||
})
|
||||
.collect::<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::<Vec<_>>();
|
||||
|
||||
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::<Vec<_>>();
|
||||
let fields_without_skip_default_from_cli_to_string = fields_without_skip_default_from_cli.iter().map(|token_stream| token_stream.to_string()).collect::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
let fields_without_skip_default_from_cli_to_string =
|
||||
fields_without_skip_default_from_cli
|
||||
.iter()
|
||||
.map(|token_stream| token_stream.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
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::<Vec<proc_macro2::TokenStream>>();
|
||||
})
|
||||
.filter(|token_stream| !token_stream.is_empty())
|
||||
.collect::<Vec<proc_macro2::TokenStream>>();
|
||||
|
||||
get_arg_for_fields
|
||||
}
|
||||
|
||||
@@ -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::<Vec<_>>()[2..];
|
||||
let group_stream =
|
||||
&group.stream().into_iter().collect::<Vec<_>>()[2..];
|
||||
output_context_dir = quote! {#(#group_stream)*};
|
||||
} else if group.stream().to_string().contains("input_context") {
|
||||
let group_stream = &group.stream()
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>()[2..];
|
||||
let group_stream =
|
||||
&group.stream().into_iter().collect::<Vec<_>>()[2..];
|
||||
input_context_dir = quote! {#(#group_stream)*};
|
||||
} else if group.stream().to_string().contains("context") {
|
||||
let group_stream = &group.stream()
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>()[2..];
|
||||
let group_stream =
|
||||
&group.stream().into_iter().collect::<Vec<_>>()[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<proc_macro2::TokenStream> = if let true = !context_dir.is_empty() {
|
||||
Some(context_dir)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let input_context_dir: Option<proc_macro2::TokenStream> = if let true = !input_context_dir.is_empty() {
|
||||
Some(input_context_dir)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let output_context_dir: Option<proc_macro2::TokenStream> = if let true = !output_context_dir.is_empty() {
|
||||
Some(output_context_dir)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let input_context_dir: Option<proc_macro2::TokenStream> =
|
||||
if let true = !input_context_dir.is_empty() {
|
||||
Some(input_context_dir)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let output_context_dir: Option<proc_macro2::TokenStream> =
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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<syn::Ident> = 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<proc_macro2::TokenStream> = Vec::new();
|
||||
let mut cfg_attr_vec: Vec<proc_macro2::TokenStream> = 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<proc_macro2::TokenStream> = Vec::new();
|
||||
let mut cfg_attr_vec: Vec<proc_macro2::TokenStream> = 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::<Vec<_>>();
|
||||
|
||||
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::<Vec<_>>();
|
||||
|
||||
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::<Vec<_>>();
|
||||
let context_scope_fields = fields
|
||||
.iter()
|
||||
.map(|field| context_scope_for_struct_field(field))
|
||||
.filter(|token_stream| !token_stream.is_empty())
|
||||
.collect::<Vec<_>>();
|
||||
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>) -> 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>,
|
||||
) -> 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<syn::Ident>) -> proc_macro2::TokenStream {
|
||||
fn for_cli_field(
|
||||
field: &syn::Field,
|
||||
ident_skip_field_vec: &Vec<syn::Ident>,
|
||||
) -> 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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<proc_macro2::TokenStream> = 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"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
pub mod to_kebab_case;
|
||||
pub mod snake_case_to_camel_case;
|
||||
pub mod to_kebab_case;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
pub fn snake_case_to_camel_case(s: String) -> String {
|
||||
let s_vec: Vec<String> = s.to_lowercase().split("_")
|
||||
let s_vec: Vec<String> = s
|
||||
.to_lowercase()
|
||||
.split("_")
|
||||
.map(|s| s.replacen(&s[..1], &s[..1].to_ascii_uppercase(), 1))
|
||||
.collect();
|
||||
s_vec.join("")
|
||||
|
||||
@@ -5,6 +5,6 @@ pub fn to_kebab_case(s: String) -> String {
|
||||
snake.push('-');
|
||||
}
|
||||
snake.push(ch.to_ascii_lowercase());
|
||||
};
|
||||
}
|
||||
snake.as_str().replace("_", "-")
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user