Skip to main content

rust_rel8_derive/
lib.rs

1use darling::{FromDeriveInput, FromField, FromTypeParam, ast, util::Flag};
2use proc_macro2::TokenStream;
3use quote::quote;
4use syn::{DeriveInput, parse_macro_input};
5
6#[derive(FromTypeParam, Debug)]
7#[darling(attributes(table))]
8struct GenericOpts {
9    ident: syn::Ident,
10    bounds: Vec<syn::TypeParamBound>,
11
12    /// Use this to mark the type param as being kept in the derived instances.
13    ///
14    /// These should come after the `'scope` lifetime and the `Mode` type param.
15    proxy: Flag,
16}
17
18#[derive(FromField, Debug)]
19#[darling(attributes(table))]
20struct FieldOpts {
21    ident: Option<syn::Ident>,
22
23    /// Marks this field as a sub table, and not a column.
24    ///
25    /// Use this when a field is another table struct.
26    sub_table: Flag,
27}
28
29#[derive(FromDeriveInput, Debug)]
30#[darling(attributes(table), supports(struct_named))]
31struct TableStructOpts {
32    ident: syn::Ident,
33    data: ast::Data<darling::util::Ignored, FieldOpts>,
34    generics: darling::ast::Generics<darling::ast::GenericParam<GenericOpts>>,
35
36    #[darling(rename = "crate", default)]
37    crate_name: Option<syn::Path>,
38}
39
40#[proc_macro_derive(TableStruct, attributes(table))]
41pub fn derive_table_struct(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
42    let a = match TableStructOpts::from_derive_input(&parse_macro_input!(input as DeriveInput)) {
43        Ok(a) => a,
44        Err(e) => {
45            return proc_macro::TokenStream::from(e.write_errors());
46        }
47    };
48
49    let r = match do_all(&a) {
50        Ok(r) => r,
51        Err(e) => {
52            return proc_macro::TokenStream::from(e.write_errors());
53        }
54    };
55
56    r.into()
57}
58
59fn do_all(a: &TableStructOpts) -> darling::Result<TokenStream> {
60    #[cfg(feature = "sqlx")]
61    let sqlx_table_loader = do_sqlx_table_loader(a)?;
62    #[cfg(not(feature = "sqlx"))]
63    let sqlx_table_loader = quote!();
64
65    let rest = do_rest(a)?;
66
67    Ok(quote! {
68        #sqlx_table_loader
69        #rest
70    })
71}
72
73#[cfg(feature = "sqlx")]
74fn do_sqlx_table_loader(t: &TableStructOpts) -> darling::Result<TokenStream> {
75    use proc_macro2::Span;
76
77    let crate_ = t.crate_name.clone().unwrap_or_else(|| {
78        let mut path = syn::Path::from(syn::Ident::new("rust_rel8", Span::call_site()));
79        path.leading_colon = Some(syn::Token![::](Span::call_site()));
80        path
81    });
82    let ident = &t.ident;
83    let proxied_type_params = t
84        .generics
85        .type_params()
86        .filter(|p| p.proxy.is_present())
87        .map(|p| p.ident.clone())
88        .collect::<Vec<_>>();
89    let proxied_type_param_bounds = t
90        .generics
91        .type_params()
92        .filter(|p| p.proxy.is_present())
93        .flat_map(|p| {
94            p.bounds.iter().map(|b| {
95                let ident = &p.ident;
96                quote! { #ident: #b}
97            })
98        })
99        .collect::<Vec<_>>();
100    let tokens = quote! {
101        impl<#(#proxied_type_params,)*> #crate_::TableLoaderSqlx for #ident<'static, #crate_::table_modes::ExprMode, #(#proxied_type_params,)*>
102        where
103            #(#proxied_type_param_bounds,)*
104        {
105            fn load<'a>(
106                &self,
107                values: &mut impl Iterator<Item = ::sqlx::any::AnyValueRef<'a>>,
108            ) -> Self::Result {
109                #crate_::TableUsingMapper::wrap_ref(self).load(values)
110            }
111
112            fn skip<'a>(&self, values: &mut impl Iterator<Item = ::sqlx::any::AnyValueRef<'a>>) {
113                #crate_::TableUsingMapper::wrap_ref(self).skip(values)
114            }
115        }
116    };
117
118    Ok(tokens)
119}
120
121fn do_rest(t: &TableStructOpts) -> darling::Result<TokenStream> {
122    use proc_macro2::Span;
123
124    let crate_ = t.crate_name.clone().unwrap_or_else(|| {
125        let mut path = syn::Path::from(syn::Ident::new("rust_rel8", Span::call_site()));
126        path.leading_colon = Some(syn::Token![::](Span::call_site()));
127        path
128    });
129    let ident = &t.ident;
130    let proxied_type_params = t
131        .generics
132        .type_params()
133        .filter(|p| p.proxy.is_present())
134        .map(|p| p.ident.clone())
135        .collect::<Vec<_>>();
136    let proxied_type_param_bounds = t
137        .generics
138        .type_params()
139        .filter(|p| p.proxy.is_present())
140        .flat_map(|p| {
141            p.bounds.iter().map(|b| {
142                let ident = &p.ident;
143                quote! { #ident: #b}
144            })
145        })
146        .collect::<Vec<_>>();
147
148    let fields = t.data.as_ref().take_struct().unwrap();
149
150    let fields = fields
151        .iter()
152        .map(|f| {
153            let ident = f.ident.as_ref().unwrap();
154            let is_sub_table = f.sub_table.is_present();
155
156            (ident, is_sub_table)
157        })
158        .collect::<Vec<_>>();
159
160    let map_modes_final_fields = fields.iter().map(|(ident, _)| quote! { #ident });
161    let map_modes_final = quote! {
162        #ident {
163            #(#map_modes_final_fields,)*
164        }
165    };
166
167    let map_modes_fields = fields.iter().map(|(ident, is_sub_table)| {
168        if *is_sub_table {
169            quote! { let #ident = self.#ident.map_modes(mapper); }
170        } else {
171            quote! { let #ident = mapper.map_mode(self.#ident); }
172        }
173    });
174
175    let map_modes_ref_fields = fields.iter().map(|(ident, is_sub_table)| {
176        if *is_sub_table {
177            quote! { let #ident = self.#ident.map_modes_ref(mapper); }
178        } else {
179            quote! { let #ident = mapper.map_mode_ref(&self.#ident); }
180        }
181    });
182
183    let map_modes_mut_fields = fields.iter().map(|(ident, is_sub_table)| {
184        if *is_sub_table {
185            quote! { let #ident = self.#ident.map_modes_mut(mapper); }
186        } else {
187            quote! { let #ident = mapper.map_mode_mut(&mut self.#ident); }
188        }
189    });
190
191    let shorten_lifetime_fields_shorten = fields
192        .iter()
193        .map(|(ident, _is_sub_table)| {
194            quote! { #ident: self.#ident.shorten_lifetime() }
195        })
196        .collect::<Vec<_>>();
197
198    let shorten_lifetime_fields_noop = fields
199        .iter()
200        .map(|(ident, _is_sub_table)| {
201            quote! { #ident: self.#ident }
202        })
203        .collect::<Vec<_>>();
204
205    let tokens = quote! {
206        impl<T: #crate_::TableMode, #(#proxied_type_params,)*> #crate_::ForLifetimeTable for #ident<'static, T, #(#proxied_type_params,)*>
207        where
208            for<'a> #ident<'a, T, #(#proxied_type_params,)*>: #crate_::Table<'a>,
209            #(#proxied_type_param_bounds,)*
210        {
211            type Of<'lt> = #ident<'lt, T, #(#proxied_type_params,)*>
212            where
213                T: 'lt,
214                #(#proxied_type_params: 'lt,)*
215                ;
216        }
217
218        impl<#(#proxied_type_params,)*> #crate_::ShortenLifetime for #ident<'static, #crate_::NameMode, #(#proxied_type_params,)*>
219        where
220            #(#proxied_type_param_bounds,)*
221        {
222            type Shortened<'small> = #ident<'small, #crate_::NameMode, #(#proxied_type_params,)*>
223            where
224                Self: 'small
225                ;
226
227            fn shorten_lifetime<'small, 'large: 'small>(self) -> Self::Shortened<'small>
228            where
229                Self: 'large,
230            {
231                #ident {
232                    #(#shorten_lifetime_fields_noop,)*
233                }
234            }
235        }
236
237        impl<#(#proxied_type_params,)*> #crate_::ShortenLifetime for #ident<'static, #crate_::ValueMode, #(#proxied_type_params,)*>
238        where
239            #(#proxied_type_param_bounds,)*
240        {
241            type Shortened<'small> = #ident<'small, #crate_::ValueMode, #(#proxied_type_params,)*>
242            where
243                Self: 'small
244                ;
245
246            fn shorten_lifetime<'small, 'large: 'small>(self) -> Self::Shortened<'small>
247            where
248                Self: 'large,
249            {
250                #ident {
251                    #(#shorten_lifetime_fields_noop,)*
252                }
253            }
254        }
255
256        impl<#(#proxied_type_params,)*> #crate_::ShortenLifetime for #ident<'static, #crate_::ValueNullifiedMode, #(#proxied_type_params,)*>
257        where
258            #(#proxied_type_param_bounds,)*
259        {
260            type Shortened<'small> = #ident<'small, #crate_::ValueNullifiedMode, #(#proxied_type_params,)*>
261            where
262                Self: 'small
263                ;
264
265            fn shorten_lifetime<'small, 'large: 'small>(self) -> Self::Shortened<'small>
266            where
267                Self: 'large,
268            {
269                #ident {
270                    #(#shorten_lifetime_fields_noop,)*
271                }
272            }
273        }
274
275        impl<#(#proxied_type_params,)*> #crate_::ShortenLifetime for #ident<'static, #crate_::EmptyMode, #(#proxied_type_params,)*>
276        where
277            #(#proxied_type_param_bounds,)*
278        {
279            type Shortened<'small> = #ident<'small, #crate_::EmptyMode, #(#proxied_type_params,)*>
280            where
281                Self: 'small
282                ;
283
284            fn shorten_lifetime<'small, 'large: 'small>(self) -> Self::Shortened<'small>
285            where
286                Self: 'large,
287            {
288                #ident {
289                    #(#shorten_lifetime_fields_noop,)*
290                }
291            }
292        }
293
294        impl<#(#proxied_type_params,)*> #crate_::ShortenLifetime for #ident<'static, #crate_::ExprMode, #(#proxied_type_params,)*>
295        where
296            #(#proxied_type_param_bounds,)*
297        {
298            type Shortened<'small> = #ident<'small, #crate_::ExprMode, #(#proxied_type_params,)*>
299            where
300                Self: 'small
301                ;
302
303            fn shorten_lifetime<'small, 'large: 'small>(self) -> Self::Shortened<'small>
304            where
305                Self: 'large,
306            {
307                #ident {
308                    #(#shorten_lifetime_fields_shorten,)*
309                }
310            }
311        }
312
313        impl<#(#proxied_type_params,)*> #crate_::ShortenLifetime for #ident<'static, #crate_::ExprNullifiedMode, #(#proxied_type_params,)*>
314        where
315            #(#proxied_type_param_bounds,)*
316        {
317            type Shortened<'small> = #ident<'small, #crate_::ExprNullifiedMode, #(#proxied_type_params,)*>
318            where
319                Self: 'small
320                ;
321
322            fn shorten_lifetime<'small, 'large: 'small>(self) -> Self::Shortened<'small>
323            where
324                Self: 'large,
325            {
326                #ident {
327                    #(#shorten_lifetime_fields_shorten,)*
328                }
329            }
330        }
331
332        impl<'scope, T: #crate_::TableMode, #(#proxied_type_params,)*> #crate_::TableHKT for #ident<'scope, T, #(#proxied_type_params,)*>
333        where
334            #(#proxied_type_param_bounds,)*
335        {
336            type Of<Mode: #crate_::TableMode> = #ident<'scope, Mode, #(#proxied_type_params,)*>;
337
338            type Mode = T;
339        }
340
341        impl<'scope, Mode: #crate_::TableMode, #(#proxied_type_params,)*> #crate_::MapTable<'scope> for #ident<'scope, Mode, #(#proxied_type_params,)*>
342        where
343            #(#proxied_type_param_bounds,)*
344        {
345            fn map_modes<Mapper, DestMode>(self, mapper: &mut Mapper) -> Self::Of<DestMode>
346            where
347                Mapper: #crate_::ModeMapper<'scope, Self::Mode, DestMode>,
348                DestMode: #crate_::TableMode,
349            {
350                #(#map_modes_fields)*
351
352                #map_modes_final
353            }
354
355            fn map_modes_ref<Mapper, DestMode>(&self, mapper: &mut Mapper) -> Self::Of<DestMode>
356            where
357                Mapper: #crate_::ModeMapperRef<'scope, Self::Mode, DestMode>,
358                DestMode: #crate_::TableMode,
359            {
360                #(#map_modes_ref_fields)*
361
362                #map_modes_final
363            }
364
365            fn map_modes_mut<Mapper, DestMode>(&mut self, mapper: &mut Mapper) -> Self::Of<DestMode>
366            where
367                Mapper: #crate_::ModeMapperMut<'scope, Self::Mode, DestMode>,
368                DestMode: #crate_::TableMode,
369            {
370                #(#map_modes_mut_fields)*
371
372                #map_modes_final
373            }
374        }
375
376        impl<'scope, #(#proxied_type_params,)*> #crate_::Table<'scope> for #ident<'scope, #crate_::table_modes::ExprMode, #(#proxied_type_params,)*>
377        where
378            <Self as #crate_::TableHKT>::Of<#crate_::table_modes::ExprNullifiedMode>: #crate_::Table<'scope>,
379            #(#proxied_type_param_bounds,)*
380        {
381            type Nullify = <Self as #crate_::TableHKT>::Of<#crate_::table_modes::ExprNullifiedMode>;
382
383            type Result = <Self as #crate_::TableHKT>::Of<#crate_::table_modes::ValueMode>;
384
385            fn visit(&self, f: &mut impl FnMut(&#crate_::ErasedExpr)) {
386                #crate_::TableUsingMapper::wrap_ref(self).visit(f)
387            }
388
389            fn visit_mut(&mut self, f: &mut impl FnMut(&mut #crate_::ErasedExpr)) {
390                #crate_::TableUsingMapper::wrap_mut(self).visit_mut(f)
391            }
392
393            fn nullify(self) -> Self::Nullify {
394                #crate_::TableUsingMapper::wrap(self).nullify()
395            }
396        }
397
398        impl<'scope, #(#proxied_type_params,)*> #crate_::Table<'scope> for #ident<'scope, #crate_::table_modes::ExprNullifiedMode, #(#proxied_type_params,)*>
399        where
400            #(#proxied_type_param_bounds,)*
401        {
402            type Nullify = Self;
403
404            type Result = <Self as #crate_::TableHKT>::Of<#crate_::table_modes::ValueNullifiedMode>;
405
406            fn visit(&self, f: &mut impl FnMut(&#crate_::ErasedExpr)) {
407                #crate_::TableUsingMapperNullified::wrap_ref(self).visit(f)
408            }
409
410            fn visit_mut(&mut self, f: &mut impl FnMut(&mut #crate_::ErasedExpr)) {
411                #crate_::TableUsingMapperNullified::wrap_mut(self).visit_mut(f)
412            }
413
414            fn nullify(self) -> Self::Nullify {
415                #crate_::TableUsingMapperNullified::wrap(self).nullify()
416            }
417        }
418    };
419
420    Ok(tokens)
421}