use proc_macro2::{Span, TokenStream};
use quote::quote;
use crate::codegen::module_path::ModulePath;
use crate::codegen::util::enum_util::to_variant_ident;
use crate::codegen::util::field_util::is_generated_field;
use crate::{TusksModule, models::Tusk};
impl TusksModule {
pub fn build_function_match_arm(
&self,
tusk: &Tusk,
cli_path: &TokenStream,
path: &ModulePath,
) -> TokenStream {
let variant_ident = to_variant_ident(&tusk.func.sig.ident);
let pattern_bindings = self.build_pattern_bindings(tusk);
let pattern_fields = self.build_pattern_fields(&pattern_bindings);
let function_call = self.build_function_call(tusk, &pattern_bindings, path, false, false);
quote! {
Some(#cli_path::Commands::#variant_ident { #(#pattern_fields),* }) => {
#function_call
}
}
}
pub fn build_default_function_match_arm(
&self,
tusk: &Tusk,
path: &ModulePath,
is_external_subcommand_case: bool,
) -> TokenStream {
let pattern_bindings = self.build_pattern_bindings(tusk);
let function_call = self.build_function_call(
tusk, &pattern_bindings, path, true, is_external_subcommand_case,
);
quote! {
None => {
#function_call
}
}
}
pub fn build_external_subcommand_match_arm(
&self,
tusk: &Tusk,
path: &ModulePath,
) -> TokenStream {
let pattern_bindings = self.build_pattern_bindings(tusk);
let function_call = self.build_function_call(
tusk, &pattern_bindings, path, false, true,
);
let cli_path = path.cli_path();
quote! {
Some(#cli_path::Commands::ClapExternalSubcommand(external_subcommand_args)) => {
#function_call
}
}
}
fn build_function_call(
&self,
tusk: &Tusk,
pattern_bindings: &[(syn::Ident, syn::Ident)],
path: &ModulePath,
is_default_case: bool,
is_external_subcommand_case: bool,
) -> TokenStream {
let func_args = self.build_function_arguments(
tusk, pattern_bindings, is_default_case, is_external_subcommand_case,
);
let func_name = &tusk.func.sig.ident;
let func_path = path.super_path_to(func_name);
let maybe_await = if tusk.is_async { quote! { .await } } else { quote! {} };
match &tusk.func.sig.output {
syn::ReturnType::Default => {
quote! { #func_path(#(#func_args),*)#maybe_await; None }
}
syn::ReturnType::Type(_, ty) => {
if Tusk::is_u8_type(ty) {
quote! { Some(#func_path(#(#func_args),*)#maybe_await) }
} else if Tusk::is_option_u8_type(ty) {
quote! { #func_path(#(#func_args),*)#maybe_await }
} else {
quote! { None }
}
}
}
}
fn build_pattern_bindings(&self, tusk: &Tusk) -> Vec<(syn::Ident, syn::Ident)> {
let skip = if self.tusk_has_parameters_arg(tusk) { 1 } else { 0 };
let mut bindings = Vec::new();
let mut counter = 1;
for param in tusk.func.sig.inputs.iter().skip(skip) {
if let syn::FnArg::Typed(pat_type) = param {
if let syn::Pat::Ident(pat_ident) = &*pat_type.pat {
let binding = syn::Ident::new(&format!("p{}", counter), Span::call_site());
bindings.push((pat_ident.ident.clone(), binding));
counter += 1;
}
}
}
bindings
}
pub fn build_pattern_fields(
&self,
pattern_bindings: &[(syn::Ident, syn::Ident)],
) -> Vec<TokenStream> {
pattern_bindings
.iter()
.filter(|(name, _)| !is_generated_field(&name.to_string()))
.map(|(name, binding)| quote! { #name: #binding })
.collect()
}
fn build_function_arguments(
&self,
tusk: &Tusk,
pattern_bindings: &[(syn::Ident, syn::Ident)],
is_default_case: bool,
is_external_subcommand_case: bool,
) -> Vec<TokenStream> {
let has_params_arg = self.tusk_has_parameters_arg(tusk);
let mut func_args = Vec::new();
let mut number_of_non_params_args = tusk.func.sig.inputs.len();
if has_params_arg {
func_args.push(quote! { ¶meters });
number_of_non_params_args -= 1;
}
if is_default_case {
if is_external_subcommand_case {
func_args.push(quote! { Vec::new() });
}
return func_args;
}
if is_external_subcommand_case && number_of_non_params_args > 0 {
func_args.push(quote! { external_subcommand_args.clone() });
return func_args;
}
for (_, binding_name) in pattern_bindings {
func_args.push(quote! { #binding_name.clone() });
}
func_args
}
}