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