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};
6
7impl TusksModule {
8 pub 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 mut parameters_struct = Self::find_parameters_struct_mut(module)?;
24
25 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 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 if m.ident == submodule_data.name {
46 return Some(m);
47 }
48 }
49 None
50 }) {
51 submodule_data.supplement_parameters(
53 item_mod,
54 false,
55 derive_debug
56 )?;
57 }
58 }
59 }
60
61 Ok(())
62 }
63
64 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 Err(syn::Error::new_spanned(
74 &item_struct.ident,
75 "Parameters struct must have a lifetime parameter"
76 ))
77 }
78
79 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 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 fn add_super_field_to_parameters_struct(
142 &mut self,
143 parameters_struct: &mut ItemStruct,
144 lifetime: &Lifetime
145 ) -> syn::Result<()> {
146 let super_type = if self.external_parent.is_some() {
148 quote! { &#lifetime parent_::Parameters<#lifetime> }
150 } else {
151 quote! { &#lifetime super::Parameters<#lifetime> }
153 };
154
155 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 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 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 ¶meters_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 let phantom_type: Type = syn::parse2(quote! {
191 ::std::marker::PhantomData<&#lifetime ()>
192 })?;
193
194 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 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}