tusks_lib/codegen/handle_matches/arms/
function.rs1use proc_macro2::{Span, TokenStream};
2use quote::{format_ident, quote};
3
4use crate::codegen::util::enum_util::convert_function_to_enum_variant;
5
6use crate::{TusksModule, models::Tusk};
7
8impl TusksModule {
9 pub fn build_function_match_arm(
29 &self,
30 tusk: &Tusk,
31 cli_path: &TokenStream,
32 path: &[&str]
33 ) -> TokenStream {
34 let variant_ident = convert_function_to_enum_variant(&tusk.func.sig.ident);
35 let pattern_bindings = self.build_pattern_bindings(tusk);
36 let pattern_fields = self.build_pattern_fields(&pattern_bindings);
37 let function_call = self.build_function_call(
38 tusk,
39 &pattern_bindings,
40 path,
41 false,
42 false
43 );
44
45 quote! {
46 Some(#cli_path::Commands::#variant_ident { #(#pattern_fields),* }) => {
47 #function_call
48 }
49 }
50 }
51
52 pub fn build_default_function_match_arm(
53 &self,
54 tusk: &Tusk,
55 path: &[&str],
56 is_external_subcommand_case: bool
57 ) -> TokenStream {
58 let pattern_bindings = self.build_pattern_bindings(tusk);
59 let function_call = self.build_function_call(
60 tusk,
61 &pattern_bindings,
62 path,
63 true,
64 is_external_subcommand_case
65 );
66
67 quote! {
68 None => {
69 #function_call
70 }
71 }
72 }
73
74 pub fn build_external_subcommand_match_arm(&self, tusk: &Tusk, path: &[&str]) -> TokenStream
75 {
76 let pattern_bindings = self.build_pattern_bindings(tusk);
77 let function_call = self.build_function_call(
78 tusk,
79 &pattern_bindings,
80 path,
81 false,
82 true
83 );
84
85 let path_tokens = path.iter().map(|segment| {
86 let ident = format_ident!("{}", segment);
87 quote! { #ident:: }
88 });
89
90 quote! {
91 Some(cli::#(#path_tokens)*Commands::ClapExternalSubcommand(external_subcommand_args)) => {
92 #function_call
93 }
94 }
95 }
96
97 fn build_function_call(
99 &self,
100 tusk: &Tusk,
101 pattern_bindings: &[(syn::Ident, syn::Ident)],
102 path: &[&str],
103 is_default_case: bool,
104 is_external_subcommand_case: bool
105 ) -> TokenStream {
106 let func_args = self.build_function_arguments(
107 tusk,
108 pattern_bindings,
109 is_default_case,
110 is_external_subcommand_case
111 );
112 let func_path = self.build_function_path(tusk, path);
113
114 match &tusk.func.sig.output {
115 syn::ReturnType::Default => {
116 quote! { #func_path(#(#func_args),*); None }
118 }
119 syn::ReturnType::Type(_, ty) => {
120 if Tusk::is_u8_type(ty) {
121 quote! { Some(#func_path(#(#func_args),*)) }
123 } else if Tusk::is_option_u8_type(ty) {
124 quote! { #func_path(#(#func_args),*) }
126 } else {
127 quote! { None }
129 }
130 }
131 }
132 }
133
134 fn build_pattern_bindings(&self, tusk: &Tusk) -> Vec<(syn::Ident, syn::Ident)> {
144 let has_params_arg = self.tusk_has_parameters_arg(tusk);
145 let skip = if has_params_arg { 1 } else { 0 };
146
147 let mut pattern_bindings = Vec::new();
148 let mut param_counter = 1;
149
150 for param in tusk.func.sig.inputs.iter().skip(skip) {
151 if let syn::FnArg::Typed(pat_type) = param {
152 if let syn::Pat::Ident(pat_ident) = &*pat_type.pat {
153 let field_name = &pat_ident.ident;
154 let binding_name = syn::Ident::new(
155 &format!("p{}", param_counter),
156 Span::call_site()
157 );
158 pattern_bindings.push((field_name.clone(), binding_name.clone()));
159 param_counter += 1;
160 }
161 }
162 }
163
164 pattern_bindings
165 }
166
167 pub fn build_pattern_fields(
176 &self,
177 pattern_bindings: &[(syn::Ident, syn::Ident)]
178 ) -> Vec<TokenStream> {
179 pattern_bindings.iter()
180 .filter(|(field_name, _)| {
181 let field_name_str = field_name.to_string();
182 field_name_str != "_phantom_lifetime_marker"
183 })
184 .map(|(field_name, binding_name)| {
185 quote! { #field_name: #binding_name }
186 })
187 .collect()
188 }
189
190 fn build_function_arguments(
200 &self,
201 tusk: &Tusk,
202 pattern_bindings: &[(syn::Ident, syn::Ident)],
203 is_default_case: bool,
204 is_external_subcommand_case: bool
205 ) -> Vec<TokenStream> {
206 let has_params_arg = self.tusk_has_parameters_arg(tusk);
207 let mut func_args = Vec::new();
208
209 let mut number_of_non_params_args = tusk.func.sig.inputs.len();
210 if has_params_arg {
211 func_args.push(quote! { ¶meters });
212 number_of_non_params_args -= 1;
213 }
214
215 if is_default_case {
216 if is_external_subcommand_case {
217 func_args.push(quote! { Vec::new() });
218 }
219 return func_args;
220 }
221
222 if is_external_subcommand_case && number_of_non_params_args > 0 {
223 func_args.push(quote! { external_subcommand_args.clone() });
224 return func_args;
225 }
226
227 for (_, binding_name) in pattern_bindings {
228 func_args.push(quote! { #binding_name.clone() });
229 }
230
231 func_args
232 }
233
234 fn build_function_path(&self, tusk: &Tusk, path: &[&str]) -> TokenStream {
242 let func_name = &tusk.func.sig.ident;
243
244 if path.is_empty() {
245 quote! { super::#func_name }
246 } else {
247 let path_idents: Vec<_> = path.iter()
248 .map(|p| syn::Ident::new(p, Span::call_site()))
249 .collect();
250 quote! { super::#(#path_idents)::*::#func_name }
251 }
252 }
253}