Skip to main content

tusks_lib/codegen/handle_matches/arms/
submodule.rs

1use proc_macro2::{Span, TokenStream};
2use quote::quote;
3
4use crate::TusksModule;
5use crate::codegen::module_path::ModulePath;
6use crate::codegen::util::enum_util::to_variant_ident;
7use crate::codegen::util::field_util::is_generated_field;
8
9impl TusksModule {
10    /// Generates a match arm for a submodule. The complete generated pattern:
11    ///
12    /// ```ignore
13    /// Some(cli::Commands::admin { user: p1, sub }) => {
14    ///     let super_parameters = &parameters;
15    ///     let parameters = super::admin::Parameters { user: p1, super_: super_parameters, .. };
16    ///     match sub {
17    ///         // ... nested arms ...
18    ///     }
19    /// }
20    /// ```
21    pub fn build_submodule_match_arm(
22        &self,
23        cli_path: &TokenStream,
24        path: &ModulePath,
25    ) -> TokenStream {
26        let variant_ident = to_variant_ident(&self.name);
27        let param_bindings = self.build_parameter_pattern_bindings();
28        let mut pattern_fields = self.build_pattern_fields(&param_bindings);
29        let has_commands = self.has_commands();
30
31        if has_commands {
32            pattern_fields.push(quote! { sub });
33        }
34
35        let params_init = self.build_parameter_initialization(&param_bindings, path, has_commands);
36        let nested_match = self.build_nested_match_arms(path, has_commands);
37
38        quote! {
39            Some(#cli_path::Commands::#variant_ident { #(#pattern_fields),* }) => {
40                #params_init
41                #nested_match
42            }
43        }
44    }
45
46    fn build_parameter_pattern_bindings(&self) -> Vec<(syn::Ident, syn::Ident)> {
47        let mut bindings = Vec::new();
48        let mut counter = 1;
49
50        if let Some(ref params) = self.parameters {
51            for field in &params.pstruct.fields {
52                if let Some(field_name) = &field.ident {
53                    if !is_generated_field(&field_name.to_string()) {
54                        let binding = syn::Ident::new(
55                            &format!("p{}", counter),
56                            Span::call_site(),
57                        );
58                        bindings.push((field_name.clone(), binding));
59                        counter += 1;
60                    }
61                }
62            }
63        }
64
65        bindings
66    }
67
68    fn has_commands(&self) -> bool {
69        !self.tusks.is_empty()
70            || !self.submodules.is_empty()
71            || !self.external_modules.is_empty()
72    }
73
74    fn build_parameter_initialization(
75        &self,
76        bindings: &[(syn::Ident, syn::Ident)],
77        path: &ModulePath,
78        has_commands: bool,
79    ) -> TokenStream {
80        if !has_commands || self.parameters.is_none() {
81            return quote! {};
82        }
83
84        let submod_name = &self.name;
85        let params = self.parameters.as_ref().unwrap();
86        let mut field_inits = Vec::new();
87
88        for field in &params.pstruct.fields {
89            if let Some(field_name) = &field.ident {
90                if field_name == "super_" {
91                    field_inits.push(quote! { super_: super_parameters, });
92                } else if field_name == "_phantom_lifetime_marker" {
93                    field_inits.push(quote! {
94                        _phantom_lifetime_marker: ::std::marker::PhantomData,
95                    });
96                } else if let Some((_, binding)) =
97                    bindings.iter().find(|(fname, _)| fname == field_name)
98                {
99                    field_inits.push(quote! { #field_name: #binding, });
100                }
101            }
102        }
103
104        let params_path = path.super_path_to(submod_name);
105
106        quote! {
107            let super_parameters = &parameters;
108            let parameters = #params_path::Parameters {
109                #(#field_inits)*
110            };
111        }
112    }
113
114    fn build_nested_match_arms(
115        &self,
116        path: &ModulePath,
117        has_commands: bool,
118    ) -> TokenStream {
119        if !has_commands {
120            return Self::build_no_command_error(path);
121        }
122
123        let new_path = path.join(&self.name.to_string());
124        let nested_arms = self.build_match_arms_recursive(&new_path);
125
126        quote! {
127            match sub {
128                #(#nested_arms)*
129            }
130        }
131    }
132}