tusks_lib/codegen/parameters/
module.rs1use 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 let lifetime = if let Some(ref params) = self.parameters {
17 Self::extract_lifetime(¶ms.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 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 if let Some(ref mut params) = self.parameters {
36 params.pstruct = parameters_struct.clone();
37 }
38
39 if let Some((_, ref mut items)) = module.content {
41 for submodule_data in &mut self.submodules {
42 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 submodule_data.supplement_parameters(
52 item_mod,
53 false,
54 derive_debug
55 )?;
56 }
57 }
58 }
59
60 Ok(())
61 }
62}
63
64impl TusksModule {
66 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 Err(syn::Error::new_spanned(
76 &item_struct.ident,
77 "Parameters struct must have a lifetime parameter"
78 ))
79 }
80
81 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 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 fn add_super_field_to_parameters_struct(
143 &mut self,
144 parameters_struct: &mut ItemStruct,
145 lifetime: &Lifetime
146 ) -> syn::Result<()> {
147 let super_type = if self.external_parent.is_some() {
149 quote! { &#lifetime parent_::Parameters<#lifetime> }
151 } else {
152 quote! { &#lifetime super::Parameters<#lifetime> }
154 };
155
156 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 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 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 ¶meters_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 let phantom_type: Type = syn::parse2(quote! {
192 ::std::marker::PhantomData<&#lifetime ()>
193 })?;
194
195 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 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}