Skip to main content

clickhouse_macros/
lib.rs

1use crate::attributes::Attributes;
2use proc_macro2::{Span, TokenStream};
3use quote::quote;
4use serde_derive_internals::{
5    Ctxt,
6    attr::{Container, Default as SerdeDefault, Field},
7};
8use syn::{Data, DataStruct, DeriveInput, Error, Fields, Lifetime, Result, parse_macro_input};
9
10mod attributes;
11
12#[cfg(test)]
13mod tests;
14
15// TODO: support wrappers `Wrapper(Inner)` and `Wrapper<T>(T)`.
16// TODO: support the `nested` attribute.
17#[proc_macro_derive(Row, attributes(clickhouse))]
18pub fn row(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
19    let input = parse_macro_input!(input as DeriveInput);
20    row_impl(input)
21        .unwrap_or_else(Error::into_compile_error)
22        .into()
23}
24
25fn column_names(data: &DataStruct, cx: &Ctxt, container: &Container) -> Result<TokenStream> {
26    Ok(match &data.fields {
27        Fields::Named(fields) => {
28            let rename_rule = container.rename_all_rules().deserialize;
29            let column_names_iter = fields
30                .named
31                .iter()
32                .enumerate()
33                .map(|(index, field)| Field::from_ast(cx, index, field, None, &SerdeDefault::None))
34                .filter(|field| !field.skip_serializing() && !field.skip_deserializing())
35                .map(|field| {
36                    rename_rule
37                        .apply_to_field(field.name().serialize_name())
38                        .to_string()
39                });
40
41            quote! {
42                &[#( #column_names_iter,)*]
43            }
44        }
45        Fields::Unnamed(_) => {
46            quote! { &[] }
47        }
48        Fields::Unit => unreachable!("checked by the caller"),
49    })
50}
51
52fn row_impl(input: DeriveInput) -> Result<TokenStream> {
53    let cx = Ctxt::new();
54
55    let Attributes { crate_path } = input.attrs[..].try_into()?;
56
57    let container = Container::from_ast(&cx, &input);
58    let name = input.ident;
59
60    let result = match &input.data {
61        Data::Struct(data) if data.fields.is_empty() => {
62            let reason = "`Row` cannot be derived for unit or empty structs";
63            Err(Error::new(name.span(), reason))
64        }
65        Data::Struct(data) => column_names(data, &cx, &container),
66        Data::Enum(_) | Data::Union(_) => {
67            let reason = "`Row` can only be derived for structs";
68            Err(Error::new(name.span(), reason))
69        }
70    };
71
72    cx.check()?;
73    let column_names = result?;
74
75    let value = match input.generics.lifetimes().count() {
76        // An owned row: `struct Row { .. }`
77        0 => quote! { Self },
78        // A borrowed row: `struct Row<'a> { .. }`
79        1 => {
80            // Replace the lifetime with `__v` to set `Value<'__v> = ..`.
81            let mut cloned = input.generics.clone();
82            let param = cloned.lifetimes_mut().next().unwrap();
83            param.lifetime = Lifetime::new("'__v", Span::call_site());
84            let ty_generics = cloned.split_for_impl().1;
85            quote! { #name #ty_generics }
86        }
87        // A borrowed row with multiple lifetimes: `struct Row<'a, 'b> { .. }`
88        _ => {
89            let lt = input.generics.lifetimes().nth(1).unwrap();
90            let reason = "`Row` cannot be derived for structs with multiple lifetimes";
91            return Err(Error::new(lt.lifetime.span(), reason));
92        }
93    };
94
95    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
96
97    Ok(quote! {
98        #[automatically_derived]
99        impl #impl_generics #crate_path::Row for #name #ty_generics #where_clause {
100            const NAME: &'static str = stringify!(#name);
101            const COLUMN_NAMES: &'static [&'static str] = #column_names;
102            const COLUMN_COUNT: usize = <Self as #crate_path::Row>::COLUMN_NAMES.len();
103            const KIND: #crate_path::_priv::RowKind = #crate_path::_priv::RowKind::Struct;
104
105            type Value<'__v> = #value;
106        }
107    })
108}