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