tusks_lib/codegen/handle_matches/
module.rs1use proc_macro2::TokenStream;
2use quote::quote;
3
4use crate::AttributeCheck;
5use crate::codegen::module_path::ModulePath;
6use crate::codegen::util::enum_util::to_variant_ident;
7
8use crate::{TusksModule, models::Tusk};
9use super::HandleMatchesCodegen;
10
11impl HandleMatchesCodegen for TusksModule {
12 fn build_handle_matches(&self, is_tusks_root: bool) -> TokenStream {
13 let maybe_async = if cfg!(feature = "async") {
14 quote! { async }
15 } else {
16 quote! {}
17 };
18
19 let signature = if is_tusks_root {
20 quote! {
21 pub #maybe_async fn handle_matches(cli: &cli::Cli) -> Option<u8>
22 }
23 } else {
24 quote! {
25 pub #maybe_async fn handle_matches(
26 cli: &cli::Cli,
27 super_parameters: &super::parent_::Parameters
28 ) -> Option<u8>
29 }
30 };
31
32 let params_init = self.build_parameters_initialization();
33 let path = ModulePath::new();
34 let match_arms = self.build_match_arms_recursive(&path);
35
36 quote! {
37 #signature {
38 #params_init
39
40 let commands = &cli.sub;
41 match commands {
42 #(#match_arms)*
43 }
44 }
45 }
46 }
47}
48
49impl TusksModule {
51 fn build_parameters_initialization(&self) -> TokenStream {
52 if let Some(ref params) = self.parameters {
53 let mut field_inits = Vec::new();
54
55 for field in ¶ms.pstruct.fields {
56 if let Some(field_name) = &field.ident {
57 let field_init = match field_name.to_string().as_str() {
58 "super_" => quote! { super_: super_parameters, },
59 "_phantom_lifetime_marker" => quote! {
60 _phantom_lifetime_marker: ::std::marker::PhantomData,
61 },
62 _ => quote! { #field_name: &cli.#field_name, },
63 };
64 field_inits.push(field_init);
65 }
66 }
67
68 quote! {
69 let parameters = super::Parameters {
70 #(#field_inits)*
71 };
72 }
73 } else {
74 quote! {}
75 }
76 }
77
78 pub fn build_match_arms_recursive(&self, path: &ModulePath) -> Vec<TokenStream> {
80 let mut arms = Vec::new();
81 let cli_path = path.cli_path();
82
83 for tusk in &self.tusks {
85 arms.push(self.build_function_match_arm(tusk, &cli_path, path));
86 }
87
88 let mut has_default_match_arm = false;
90 for tusk in &self.tusks {
91 if tusk.func.has_attr("default") {
92 arms.push(self.build_default_function_match_arm(
93 tusk,
94 path,
95 self.allow_external_subcommands
96 ));
97 if self.allow_external_subcommands {
98 arms.push(self.build_external_subcommand_match_arm(tusk, path));
99 }
100 has_default_match_arm = true;
101 break;
102 }
103 }
104
105 if !has_default_match_arm {
106 arms.push(Self::build_no_command_error_arm(path));
107 }
108
109 for submodule in &self.submodules {
111 arms.push(submodule.build_submodule_match_arm(&cli_path, path));
112 }
113
114 if !self.external_modules.is_empty() {
116 arms.push(self.build_external_arm(&cli_path, path));
117 }
118
119 arms
120 }
121
122 pub fn build_no_command_error(path: &ModulePath) -> TokenStream {
123 if let Some(last) = path.last() {
124 quote! {
125 eprintln!("Subcommand required! Please provide a subcommand for {}!", #last);
126 Some(1)
127 }
128 } else {
129 quote! {
130 eprintln!("Command required! Please provide a command!");
131 Some(1)
132 }
133 }
134 }
135
136 fn build_no_command_error_arm(path: &ModulePath) -> TokenStream {
137 let error = Self::build_no_command_error(path);
138 quote! {
139 None => {
140 #error
141 }
142 }
143 }
144
145 fn build_external_arm(&self, cli_path: &TokenStream, path: &ModulePath) -> TokenStream {
146 let mut external_arms = Vec::new();
147 let maybe_await = if cfg!(feature = "async") { quote! { .await } } else { quote! {} };
148
149 for ext_mod in &self.external_modules {
150 let alias = &ext_mod.alias;
151 let variant_ident = to_variant_ident(alias);
152 let external_path = path.super_path_to(alias);
153
154 external_arms.push(quote! {
155 #cli_path::ExternalCommands::#variant_ident(cli) => {
156 #external_path::__internal_tusks_module::handle_matches(cli, ¶meters)#maybe_await
157 }
158 });
159 }
160
161 quote! {
162 Some(#cli_path::Commands::TuskExternalCommands(commands)) => {
163 match commands {
164 #(#external_arms)*
165 }
166 }
167 }
168 }
169
170 pub fn tusk_has_parameters_arg(&self, tusk: &Tusk) -> bool {
171 if let Some(syn::FnArg::Typed(first_param)) = tusk.func.sig.inputs.first()
172 && let Some(ref params) = self.parameters {
173 return Self::is_parameters_type(&first_param.ty, ¶ms.pstruct.ident);
174 }
175 false
176 }
177}