Skip to main content

tusks_lib/codegen/parameters/
module.rs

1use syn::{Field, Fields, GenericParam, ItemMod, ItemStruct, Lifetime, Type};
2use quote::quote;
3use proc_macro2::Span;
4
5use crate::{TusksModule, models::TusksParameters};
6use super::ParametersCodegen;
7
8impl ParametersCodegen for TusksModule {
9    fn supplement_parameters(
10        &mut self, 
11        module: &mut ItemMod, 
12        is_tusks_root: bool,
13        derive_debug: bool,
14    ) -> syn::Result<()> {
15        // 1. Get or create Parameters struct with lifetime
16        let lifetime = if let Some(ref params) = self.parameters {
17            // Extract lifetime from existing struct
18            Self::extract_lifetime(&params.pstruct)?
19        } else {
20            self.add_parameters_struct(module, derive_debug)?
21        };
22
23        let mut parameters_struct = Self::find_parameters_struct_mut(module)?;
24        
25        // 2. Add super_ field if needed
26        if !is_tusks_root {
27            self.add_super_field_to_parameters_struct(
28                &mut parameters_struct,
29                &lifetime)?;
30        }
31
32        Self::add_phantom_field_to_struct(&mut parameters_struct, &lifetime)?;
33
34        // Update our internal structure
35        if let Some(ref mut params) = self.parameters {
36            params.pstruct = parameters_struct.clone();
37        }
38        
39        // 3. Recursively process submodules
40        if let Some((_, ref mut items)) = module.content {
41            for submodule_data in &mut self.submodules {
42                // Find corresponding ItemMod in module items
43                if let Some(item_mod) = items.iter_mut().find_map(|item| {
44                    if let syn::Item::Mod(m) = item {
45                        if m.ident == submodule_data.name {
46                            return Some(m);
47                        }
48                    }
49                    None
50                }) {
51                    // Recursively supplement (submodules are never tusks root)
52                    submodule_data.supplement_parameters(
53                        item_mod,
54                        false,
55                        derive_debug
56                    )?;
57                }
58            }
59        }
60        
61        Ok(())
62    }
63}
64
65/// Internal helpers for parameter supplementation.
66impl TusksModule {
67    /// Extract the first lifetime parameter from a struct
68    pub fn extract_lifetime(item_struct: &ItemStruct) -> syn::Result<Lifetime> {
69        for param in &item_struct.generics.params {
70            if let GenericParam::Lifetime(lifetime_param) = param {
71                return Ok(lifetime_param.lifetime.clone());
72            }
73        }
74        
75        // If no lifetime found, return error
76        Err(syn::Error::new_spanned(
77            &item_struct.ident,
78            "Parameters struct must have a lifetime parameter"
79        ))
80    }
81    
82    /// Create a new empty Parameters struct with the given lifetime
83    fn add_parameters_struct(
84        &mut self,
85        module: &mut ItemMod, 
86        derive_debug: bool
87    ) -> syn::Result<Lifetime> {
88        let lifetime = Lifetime::new("'a", Span::call_site());
89
90        let lifetime_param = { quote! {<#lifetime>} };
91
92        let derive_attr =
93            if derive_debug { quote! { #[derive(Debug)] } }
94            else { quote! {} };
95
96        let tokens = quote! {
97            #derive_attr
98            pub struct Parameters #lifetime_param {
99            }
100        };
101
102        let params_struct: ItemStruct = syn::parse2(tokens).map_err(|e| {
103            syn::Error::new(Span::call_site(), format!("Failed to create Parameters struct: {}", e))
104        })?;
105
106        Self::add_struct_to_module(module, params_struct.clone())?;
107
108        self.parameters = Some(TusksParameters{ pstruct: params_struct });
109
110        Ok(lifetime)
111    }
112    
113    /// Add a struct to the module's items
114    fn add_struct_to_module(module: &mut ItemMod, item_struct: ItemStruct) -> syn::Result<()> {
115        if let Some((_, ref mut items)) = module.content {
116            items.insert(0, syn::Item::Struct(item_struct));
117            Ok(())
118        } else {
119            Err(syn::Error::new_spanned(
120                &module.ident,
121                "Module has no content"
122            ))
123        }
124    }
125    
126    fn find_parameters_struct_mut(module: &mut ItemMod) -> syn::Result<&mut ItemStruct> {
127        if let Some((_, ref mut items)) = module.content {
128            for item in items.iter_mut() {
129                if let syn::Item::Struct(s) = item {
130                    if s.ident == "Parameters" {
131                        return Ok(s);
132                    }
133                }
134            }
135        }
136
137        Err(syn::Error::new_spanned(
138            &module.ident,
139            "Parameters struct not found in module"
140        ))
141    }
142
143    /// Add super_ field to the Parameters struct in the module
144    fn add_super_field_to_parameters_struct(
145        &mut self,
146        parameters_struct: &mut ItemStruct,
147        lifetime: &Lifetime
148    ) -> syn::Result<()> {
149        // Determine the type of super_ based on whether we have an external parent
150        let super_type = if self.external_parent.is_some() {
151            // Local root with external parent: &'lifetime parent_::Parameters<'lifetime>
152            quote! { &#lifetime parent_::Parameters<#lifetime> }
153        } else {
154            // Submodule: &'lifetime super::Parameters<'lifetime>
155            quote! { &#lifetime super::Parameters<#lifetime> }
156        };
157
158        // Parse the type
159        let super_field_type: Type = syn::parse2(super_type).map_err(|e| {
160            syn::Error::new(Span::call_site(), format!("Failed to parse super_ type: {}", e))
161        })?;
162
163        // Create the super_ field
164        let super_field = Field {
165            attrs: vec![],
166            vis: syn::Visibility::Public(syn::token::Pub::default()),
167            mutability: syn::FieldMutability::None,
168            ident: Some(syn::Ident::new("super_", Span::call_site())),
169            colon_token: Some(syn::token::Colon::default()),
170            ty: super_field_type,
171        };
172
173        // Add super_ field
174        if let Fields::Named(ref mut fields) = parameters_struct.fields {
175            fields.named.push(super_field);
176        } else {
177            return Err(syn::Error::new_spanned(
178                &parameters_struct.ident,
179                "Parameters struct must have named fields"
180            ));
181        }
182
183        Ok(())
184    }
185
186    fn add_phantom_field_to_struct(
187        item_struct: &mut ItemStruct,
188        lifetime: &Lifetime
189    ) -> syn::Result<()> {
190        use syn::{Field, Type, Ident};
191
192        // Parse nur den Typ
193        let phantom_type: Type = syn::parse2(quote! {
194            ::std::marker::PhantomData<&#lifetime ()>
195        })?;
196
197        // Konstruiere das Field direkt
198        let phantom_field = Field {
199            attrs: vec![],
200            vis: syn::Visibility::Public(syn::token::Pub::default()),
201            mutability: syn::FieldMutability::None,
202            ident: Some(Ident::new("_phantom_lifetime_marker", Span::call_site())),
203            colon_token: Some(Default::default()),
204            ty: phantom_type,
205        };
206
207        // Füge das Feld zu den struct fields hinzu
208        match &mut item_struct.fields {
209            syn::Fields::Named(fields) => {
210                fields.named.push(phantom_field);
211            }
212            syn::Fields::Unnamed(_) => {
213                return Err(syn::Error::new_spanned(
214                    item_struct,
215                    "Cannot add named field to tuple struct"
216                ));
217            }
218            syn::Fields::Unit => {
219                item_struct.fields = syn::Fields::Named(syn::FieldsNamed {
220                    brace_token: Default::default(),
221                    named: {
222                        let mut fields = syn::punctuated::Punctuated::new();
223                        fields.push(phantom_field);
224                        fields
225                    }
226                });
227            }
228        }
229
230        Ok(())
231    }
232}