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