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 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
65impl TusksModule {
67 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 Err(syn::Error::new_spanned(
77 &item_struct.ident,
78 "Parameters struct must have a lifetime parameter"
79 ))
80 }
81
82 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 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 fn add_super_field_to_parameters_struct(
145 &mut self,
146 parameters_struct: &mut ItemStruct,
147 lifetime: &Lifetime
148 ) -> syn::Result<()> {
149 let super_type = if self.external_parent.is_some() {
151 quote! { &#lifetime parent_::Parameters<#lifetime> }
153 } else {
154 quote! { &#lifetime super::Parameters<#lifetime> }
156 };
157
158 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 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 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 ¶meters_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 let phantom_type: Type = syn::parse2(quote! {
194 ::std::marker::PhantomData<&#lifetime ()>
195 })?;
196
197 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 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}