1use proc_macro2::{Ident, Span, TokenStream};
12use quote::quote;
13
14use crate::modifiers::Modifiers;
15use crate::shape::{Modifier, Shape};
16use crate::signature;
17use crate::source::sqlfunc_source;
18
19fn override_methods(
27 shape: Shape,
28 mods: &Modifiers,
29 mut introduces_nulls_fn: Option<TokenStream>,
30) -> Vec<TokenStream> {
31 shape
32 .modifiers()
33 .iter()
34 .filter_map(|(modifier, ret)| {
35 if *modifier == Modifier::IntroducesNulls {
36 return introduces_nulls_fn.take();
37 }
38 let expr = mods.get(*modifier)?;
39 let name = Ident::new(modifier.name(), Span::call_site());
40 let body = ret.body(expr);
41 let ret = ret.tokens();
42 Some(quote! {
43 fn #name(&self) -> #ret {
44 #body
45 }
46 })
47 })
48 .collect()
49}
50
51struct Codegen {
53 struct_name: Ident,
54 has_self: bool,
57 sqlname: TokenStream,
58 source: TokenStream,
61 skip_display: bool,
64}
65
66fn expand(e: &Codegen, func: &syn::ItemFn, trait_impl: TokenStream) -> TokenStream {
70 let func_name = &func.sig.ident;
71 let Codegen {
72 struct_name,
73 has_self,
74 sqlname,
75 source,
76 skip_display,
77 } = e;
78
79 let display_impl = if *skip_display {
80 quote! {}
81 } else {
82 quote! {
83 impl std::fmt::Display for #struct_name {
84 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
85 f.write_str(#sqlname)
86 }
87 }
88 }
89 };
90
91 let funcname_impl = quote! {
92 impl crate::func::FuncName for #struct_name {
93 const NAME: &'static str = stringify!(#func_name);
94 #source
95 }
96 };
97
98 if *has_self {
99 quote! {
100 impl #struct_name {
101 #func
102 }
103 #trait_impl
104 #display_impl
105 #funcname_impl
106 }
107 } else {
108 quote! {
109 #[derive(
110 Ord, PartialOrd, Clone,
111 Debug, Eq, PartialEq, serde::Serialize,
112 serde::Deserialize, Hash,
113 )]
114 #[cfg_attr(any(test, feature = "proptest"), derive(proptest_derive::Arbitrary))]
115 pub struct #struct_name;
116
117 #trait_impl
118 #display_impl
119 #funcname_impl
120
121 #func
122 }
123 }
124}
125
126fn sql_params(shape: Shape, func: &syn::ItemFn) -> darling::Result<(Vec<syn::Type>, Vec<Ident>)> {
131 const POSITIONAL: [&str; 2] = ["a", "b"];
133 let mut types = Vec::new();
134 let mut names = Vec::new();
135 for (position, param) in signature::sql_args(func).into_iter().enumerate() {
136 let syn::FnArg::Typed(pat) = param else {
137 return Err(darling::Error::custom("unexpected self parameter").with_span(param));
138 };
139 let name = match shape {
140 Shape::Unary | Shape::Binary => Ident::new(POSITIONAL[position], Span::call_site()),
141 Shape::Variadic => {
142 let syn::Pat::Ident(ident) = &*pat.pat else {
143 return Err(
144 darling::Error::custom("unsupported parameter pattern").with_span(&pat.pat)
145 );
146 };
147 ident.ident.clone()
148 }
149 };
150 types.push(signature::patch_lifetimes(&pat.ty));
151 names.push(name);
152 }
153 Ok((types, names))
154}
155
156fn output_type_and_introduces_nulls(
163 shape: Shape,
164 mods: &Modifiers,
165 param_types_raw: &[syn::Type],
166 output_ty_raw: &syn::Type,
167 generic_params: &[Ident],
168) -> darling::Result<(TokenStream, Option<TokenStream>)> {
169 let output_type = mods.output_type();
170 let mut output_type_expr = mods.output_type_expr().map(|expr| quote! { #expr });
171 let mut introduces_nulls = mods
172 .get(Modifier::IntroducesNulls)
173 .map(|expr| quote! { #expr });
174
175 if !generic_params.is_empty() && output_type.is_none() && output_type_expr.is_none() {
178 if let Some(derived) = signature::derive_output_type_for_generics(
179 param_types_raw,
180 output_ty_raw,
181 generic_params,
182 shape == Shape::Unary,
183 )? {
184 output_type_expr = Some(derived);
185 if introduces_nulls.is_none() {
186 let nullable = signature::is_option_wrapped(output_ty_raw);
187 introduces_nulls = Some(quote! { #nullable });
188 }
189 }
190 }
191
192 let column_type = match (output_type, output_type_expr) {
193 (Some(output_type), _) => quote! { <#output_type>::as_column_type() },
194 (None, Some(output_type_expr)) => output_type_expr,
195 (None, None) => quote! { Self::Output::as_column_type() },
196 };
197 let introduces_nulls = introduces_nulls.or_else(|| {
200 output_type.map(|output_type| {
201 quote! { <#output_type as ::mz_repr::OutputDatumType<'_, ()>>::nullable() }
202 })
203 });
204 let introduces_nulls_fn = introduces_nulls.map(|introduces_nulls| {
205 quote! {
206 fn introduces_nulls(&self) -> bool {
207 #introduces_nulls
208 }
209 }
210 });
211 Ok((column_type, introduces_nulls_fn))
212}
213
214pub(crate) fn generate(
219 shape: Shape,
220 func: &syn::ItemFn,
221 mods: Modifiers,
222 struct_ty: Option<syn::Path>,
223 attr: &TokenStream,
224) -> darling::Result<TokenStream> {
225 let fn_name = &func.sig.ident;
226 let struct_name = struct_ty
227 .as_ref()
228 .and_then(|ty| ty.segments.last())
229 .map_or_else(|| signature::camel_case(fn_name), |seg| seg.ident.clone());
230
231 let has_self = signature::has_receiver(func);
234 mods.validate(shape, has_self)?;
235 let arena = signature::last_is_arena(func);
238 let output_ty_raw = signature::output_type(func)?;
239 let generic_params = signature::find_generic_type_params(func);
240
241 let (param_types_raw, param_names) = sql_params(shape, func)?;
242
243 let param_types: Vec<syn::Type> = param_types_raw
246 .iter()
247 .map(|ty| signature::erase_all_generic_params(ty, &generic_params))
248 .collect();
249 let output_ty = signature::erase_all_generic_params(output_ty_raw, &generic_params);
250
251 let input_ty: syn::Type = if let [ty] = param_types.as_slice() {
253 ty.clone()
254 } else {
255 syn::parse_quote! { (#(#param_types),*) }
256 };
257 let destructure = if let [name] = param_names.as_slice() {
258 quote! { #name }
259 } else {
260 quote! { (#(#param_names),*) }
261 };
262
263 let (output_type_code, introduces_nulls_fn) = output_type_and_introduces_nulls(
264 shape,
265 &mods,
266 ¶m_types_raw,
267 output_ty_raw,
268 &generic_params,
269 )?;
270 let methods = override_methods(shape, &mods, introduces_nulls_fn);
271
272 let arena_arg = if arena {
273 quote! { , temp_storage }
274 } else {
275 quote! {}
276 };
277 let receiver = if has_self {
278 quote! { self. }
279 } else {
280 quote! {}
281 };
282
283 let trait_path = shape.trait_path();
284 let (output_method, output_method_param) = shape.output_method();
285 let nullability = shape.nullability(&signature::non_nullable_position_checks(¶m_types));
286
287 let trait_impl = quote! {
288 impl #trait_path for #struct_name {
289 type Input<'a> = #input_ty;
290 type Output<'a> = #output_ty;
291
292 fn call<'a>(
293 &self,
294 #destructure: Self::Input<'a>,
295 temp_storage: &'a mz_repr::RowArena,
296 ) -> Self::Output<'a> {
297 #receiver #fn_name(#(#param_names),* #arena_arg)
298 }
299
300 fn #output_method(
301 &self,
302 #output_method_param
303 ) -> mz_repr::SqlColumnType {
304 use mz_repr::AsColumnType;
305 let output = #output_type_code;
306 let propagates_nulls = #trait_path::propagates_nulls(self);
307 let nullable = output.nullable;
308 #nullability
309 }
310
311 #(#methods)*
312 }
313 };
314
315 let sqlname = mods
316 .sqlname()
317 .map_or_else(|| quote! { stringify!(#fn_name) }, |name| quote! { #name });
318 let codegen = Codegen {
319 struct_name,
320 has_self,
321 sqlname,
322 source: sqlfunc_source(attr, func, ¶m_types_raw, output_ty_raw, ¶m_types),
323 skip_display: mods.skip_display(),
324 };
325 Ok(expand(&codegen, func, trait_impl))
326}