1use quote::{ToTokens, format_ident, quote, quote_spanned};
4use syn::{FnArg, Ident, PatType, Path, Receiver, ReturnType, Type, parse_quote, spanned::Spanned};
5
6use crate::Cycle;
7
8pub(crate) struct TrackedQuery {
9 pub(crate) trait_name: Ident,
10 pub(crate) signature: syn::Signature,
11 pub(crate) pat_and_tys: Vec<PatType>,
12 pub(crate) invoke: Option<Path>,
13 pub(crate) default: Option<syn::Block>,
14 pub(crate) cycle: Option<Cycle>,
15 pub(crate) lru: Option<u32>,
16 pub(crate) generated_struct: Option<GeneratedInputStruct>,
17}
18
19pub(crate) struct GeneratedInputStruct {
20 pub(crate) input_struct_name: Ident,
21 pub(crate) create_data_ident: Ident,
22}
23
24impl ToTokens for TrackedQuery {
25 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
26 let sig = &self.signature;
27 let trait_name = &self.trait_name;
28
29 let ret = &sig.output;
30
31 let invoke = match &self.invoke {
32 Some(path) => path.to_token_stream(),
33 None => sig.ident.to_token_stream(),
34 };
35
36 let fn_ident = &sig.ident;
37 let shim: Ident = format_ident!("{}_shim", fn_ident);
38
39 let options = self
40 .cycle
41 .as_ref()
42 .map(|Cycle { cycle_fn, cycle_initial, cycle_result }| {
43 let cycle_fn = cycle_fn.as_ref().map(|(ident, path)| quote!(#ident=#path));
44 let cycle_initial =
45 cycle_initial.as_ref().map(|(ident, path)| quote!(#ident=#path));
46 let cycle_result = cycle_result.as_ref().map(|(ident, path)| quote!(#ident=#path));
47 let options = cycle_fn.into_iter().chain(cycle_initial).chain(cycle_result);
48 quote!(#(#options),*)
49 })
50 .into_iter()
51 .chain(self.lru.map(|lru| quote!(lru = #lru)))
52 .chain(Some(quote!(unsafe(non_update_return_type))));
53 let annotation = quote!(#[salsa_macros::tracked( #(#options),* )]);
54
55 let pat_and_tys = &self.pat_and_tys;
56 let params = self
57 .pat_and_tys
58 .iter()
59 .map(|pat_type| pat_type.pat.clone())
60 .collect::<Vec<Box<syn::Pat>>>();
61
62 let invoke_block = match &self.default {
63 Some(default) => quote! { #default },
64 None => {
65 let invoke_params: proc_macro2::TokenStream = quote! {db, #(#params),*};
66 quote_spanned! { invoke.span() => {#invoke(#invoke_params)}}
67 }
68 };
69
70 let method = match &self.generated_struct {
71 Some(generated_struct) => {
72 let input_struct_name = &generated_struct.input_struct_name;
73 let create_data_ident = &generated_struct.create_data_ident;
74
75 quote! {
76 #sig {
77 #annotation
78 fn #shim<'db>(
79 db: &'db dyn #trait_name,
80 _input: #input_struct_name,
81 #(#pat_and_tys),*
82 ) #ret
83 #invoke_block
84 #shim(self, #create_data_ident(self), #(#params),*)
85 }
86 }
87 }
88 None => {
89 quote! {
90 #sig {
91 #annotation
92 fn #shim<'db>(
93 db: &'db dyn #trait_name,
94 #(#pat_and_tys),*
95 ) #ret
96 #invoke_block
97
98 #shim(self, #(#params),*)
99 }
100 }
101 }
102 };
103
104 method.to_tokens(tokens);
105 }
106}
107
108pub(crate) struct InputQuery {
109 pub(crate) signature: syn::Signature,
110 pub(crate) create_data_ident: Ident,
111}
112
113impl ToTokens for InputQuery {
114 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
115 let sig = &self.signature;
116 let fn_ident = &sig.ident;
117 let create_data_ident = &self.create_data_ident;
118
119 let method = quote! {
120 #sig {
121 let data = #create_data_ident(self);
122 data.#fn_ident(self).unwrap()
123 }
124 };
125 method.to_tokens(tokens);
126 }
127}
128
129pub(crate) struct InputSetter {
130 pub(crate) signature: syn::Signature,
131 pub(crate) return_type: syn::Type,
132 pub(crate) create_data_ident: Ident,
133}
134
135impl ToTokens for InputSetter {
136 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
137 let sig = &mut self.signature.clone();
138
139 let ty = &self.return_type;
140 let fn_ident = &sig.ident;
141 let create_data_ident = &self.create_data_ident;
142
143 let setter_ident = format_ident!("set_{}", fn_ident);
144 sig.ident = setter_ident.clone();
145
146 let value_argument: PatType = parse_quote!(__value: #ty);
147 sig.inputs.push(FnArg::Typed(value_argument.clone()));
148
149 let mut_receiver: Receiver = parse_quote!(&mut self);
151 if let Some(og) = sig.inputs.first_mut() {
152 *og = FnArg::Receiver(mut_receiver)
153 }
154
155 sig.output = ReturnType::Default;
157
158 let value = &value_argument.pat;
159 let method = quote! {
160 #sig {
161 use salsa::Setter;
162 let data = #create_data_ident(self);
163 data.#setter_ident(self).to(Some(#value));
164 }
165 };
166 method.to_tokens(tokens);
167 }
168}
169
170pub(crate) struct InputSetterWithDurability {
171 pub(crate) signature: syn::Signature,
172 pub(crate) return_type: syn::Type,
173 pub(crate) create_data_ident: Ident,
174}
175
176impl ToTokens for InputSetterWithDurability {
177 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
178 let sig = &mut self.signature.clone();
179
180 let ty = &self.return_type;
181 let fn_ident = &sig.ident;
182 let setter_ident = format_ident!("set_{}", fn_ident);
183
184 let create_data_ident = &self.create_data_ident;
185
186 sig.ident = format_ident!("set_{}_with_durability", fn_ident);
187
188 let value_argument: PatType = parse_quote!(__value: #ty);
189 sig.inputs.push(FnArg::Typed(value_argument.clone()));
190
191 let durability_argument: PatType = parse_quote!(durability: salsa::Durability);
192 sig.inputs.push(FnArg::Typed(durability_argument.clone()));
193
194 let mut_receiver: Receiver = parse_quote!(&mut self);
196 if let Some(og) = sig.inputs.first_mut() {
197 *og = FnArg::Receiver(mut_receiver)
198 }
199
200 sig.output = ReturnType::Default;
202
203 let value = &value_argument.pat;
204 let durability = &durability_argument.pat;
205 let method = quote! {
206 #sig {
207 use salsa::Setter;
208 let data = #create_data_ident(self);
209 data.#setter_ident(self)
210 .with_durability(#durability)
211 .to(Some(#value));
212 }
213 };
214 method.to_tokens(tokens);
215 }
216}
217
218pub(crate) enum SetterKind {
219 Plain(InputSetter),
220 WithDurability(InputSetterWithDurability),
221}
222
223impl ToTokens for SetterKind {
224 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
225 match self {
226 SetterKind::Plain(input_setter) => input_setter.to_tokens(tokens),
227 SetterKind::WithDurability(input_setter_with_durability) => {
228 input_setter_with_durability.to_tokens(tokens)
229 }
230 }
231 }
232}
233
234pub(crate) struct Transparent {
235 pub(crate) signature: syn::Signature,
236 pub(crate) pat_and_tys: Vec<PatType>,
237 pub(crate) invoke: Option<Path>,
238 pub(crate) default: Option<syn::Block>,
239}
240
241impl ToTokens for Transparent {
242 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
243 let sig = &self.signature;
244
245 let ty = self
246 .pat_and_tys
247 .iter()
248 .map(|pat_type| pat_type.pat.clone())
249 .collect::<Vec<Box<syn::Pat>>>();
250
251 let invoke = match &self.invoke {
252 Some(path) => path.to_token_stream(),
253 None => sig.ident.to_token_stream(),
254 };
255
256 let method = match &self.default {
257 Some(default) => quote! {
258 #sig { let db = self; #default }
259 },
260 None => quote! {
261 #sig {
262 #invoke(self, #(#ty),*)
263 }
264 },
265 };
266
267 method.to_tokens(tokens);
268 }
269}
270pub(crate) struct Intern {
271 pub(crate) signature: syn::Signature,
272 pub(crate) pat_and_tys: Vec<PatType>,
273 pub(crate) interned_struct_path: Path,
274}
275
276impl ToTokens for Intern {
277 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
278 let sig = &self.signature;
279
280 let ty = self.pat_and_tys.to_vec();
281
282 let interned_pat = ty.first().expect("at least one pat; this is a bug");
283 let interned_pat = &interned_pat.pat;
284
285 let wrapper_struct = self.interned_struct_path.to_token_stream();
286
287 let method = quote! {
288 #sig {
289 #wrapper_struct::new(self, #interned_pat)
290 }
291 };
292
293 method.to_tokens(tokens);
294 }
295}
296
297pub(crate) struct Lookup {
298 pub(crate) signature: syn::Signature,
299 pub(crate) pat_and_tys: Vec<PatType>,
300 pub(crate) return_ty: Type,
301 pub(crate) interned_struct_path: Path,
302}
303
304impl Lookup {
305 pub(crate) fn prepare_signature(&mut self) {
306 let sig = &self.signature;
307
308 let ident = format_ident!("lookup_{}", sig.ident);
309
310 let ty = self.pat_and_tys.to_vec();
311
312 let interned_key = &self.return_ty;
313
314 let interned_pat = ty.first().expect("at least one pat; this is a bug");
315 let interned_return_ty = &interned_pat.ty;
316
317 self.signature = parse_quote!(
318 fn #ident(&self, id: #interned_key) -> #interned_return_ty
319 );
320 }
321}
322
323impl ToTokens for Lookup {
324 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
325 let sig = &self.signature;
326
327 let wrapper_struct = self.interned_struct_path.to_token_stream();
328 let method = quote! {
329 #sig {
330 let zalsa = self.zalsa();
331 #wrapper_struct::ingredient(zalsa).data(zalsa, id.as_id()).0.clone()
332 }
333 };
334
335 method.to_tokens(tokens);
336 }
337}
338
339#[allow(clippy::large_enum_variant)]
340pub(crate) enum Queries {
341 TrackedQuery(TrackedQuery),
342 InputQuery(InputQuery),
343 Intern(Intern),
344 Transparent(Transparent),
345}
346
347impl ToTokens for Queries {
348 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
349 match self {
350 Queries::TrackedQuery(tracked_query) => tracked_query.to_tokens(tokens),
351 Queries::InputQuery(input_query) => input_query.to_tokens(tokens),
352 Queries::Transparent(transparent) => transparent.to_tokens(tokens),
353 Queries::Intern(intern) => intern.to_tokens(tokens),
354 }
355 }
356}