1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4 parse::Parser, parse_macro_input, punctuated::Punctuated, Data, DeriveInput, Field, Fields,
5 Token, Type,
6};
7
8#[proc_macro_attribute]
18pub fn table_common_fields(args: TokenStream, input: TokenStream) -> TokenStream {
19 let args_parsed = parse_macro_input!(args as CommaSeparatedTypes);
20 let owner_type = args_parsed
21 .types
22 .first()
23 .cloned()
24 .unwrap_or(Type::Verbatim(quote! { u64 }));
25 let time_type = args_parsed
26 .types
27 .last()
28 .cloned()
29 .unwrap_or(Type::Verbatim(quote! { String }));
30
31 let input = parse_macro_input!(input as DeriveInput);
32 let struct_name = &input.ident;
33
34 let expanded = match input.data {
35 Data::Struct(mut data_struct) => {
36 if let Fields::Named(ref mut named_fields) = data_struct.fields {
37 let fields_to_add = vec![
38 ("created_by", quote! { pub created_by: #owner_type }),
39 ("updated_by", quote! { pub updated_by: #owner_type }),
40 ("created_at", quote! { pub created_at: #time_type }),
41 ("updated_at", quote! { pub updated_at: #time_type }),
42 ("is_deleted", quote! { pub is_deleted: bool }),
43 ];
44
45 for (field_name, field_quote) in fields_to_add {
46 let already_has_field = named_fields.named.iter().any(|field| {
47 if let Some(ident) = &field.ident {
48 ident == field_name
49 } else {
50 false
51 }
52 });
53
54 if !already_has_field {
55 named_fields
56 .named
57 .push(Field::parse_named.parse2(field_quote).unwrap());
58 }
59 }
60 }
61
62 let fields = &data_struct.fields;
63 let attrs = &input.attrs;
64 quote! {
65 #(#attrs)*
66 pub struct #struct_name #fields
67 }
68 }
69 _ => syn::Error::new_spanned(input, "AddField can only be used on structs.")
70 .to_compile_error(),
71 };
72
73 TokenStream::from(expanded)
74}
75
76struct CommaSeparatedTypes {
77 types: Punctuated<Type, Token![,]>,
78}
79
80impl syn::parse::Parse for CommaSeparatedTypes {
82 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
83 Ok(CommaSeparatedTypes {
84 types: Punctuated::parse_terminated(input)?,
85 })
86 }
87}