reindeer_macros/
lib.rs

1//! Derive macro for `reindeer`'s 🦌 `Entity` trait.
2//! 
3//! To automatically derive Entity on a `struct`, you simply have to derive `Entity` (as Well as `serde`'s `Serialize` and `Deserialize` traits) like so:
4//! 
5//! ```rust
6//! #[derive(Serialize,Deserialize,Entity)]
7//! struct User {
8//!     id : (u32,u32),
9//!     email : String,
10//!     username : String,
11//!     last_login : i64,
12//!     password_hash : String,
13//! }
14//! ```
15//! 
16//! ☝😉 This will generate an `Entity` implementation with store name `User`, version 0, and id being the `id` field.
17//! To specify other values, use the helper attribute `entity` like so :
18//! 
19//! ```rust
20//! #[derive(Serialize,Deserialize,Entity)]
21//! #[entity(name = "user", version = 1,id = "email")]
22//! struct User {
23//!     email : String,
24//!     username : String,
25//!     last_login : i64,
26//!     password_hash : String,
27//! }
28//! ```
29//! 
30//! To specify sibling entities and child entities, use the `sibling` and `child` helper attributes
31//! respectively:
32//! 
33//! ```rust
34//! #[derive(Serialize,Deserialize,Entity)]
35//! #[entity(name = "user", version = 1,id = "email")]
36//! #[sibling(("user_data", Cascade))]
37//! #[children(("doc",Cascade),("shared_doc",BreakLink))]
38//! struct User {
39//!     email : String,
40//!     username : String,
41//!     last_login : i64,
42//!     password_hash : String,
43//! }
44//! 
45//! //! #[derive(Serialize,Deserialize,Entity)]
46//! #[entity(name = "user_data", version = 1,id = "email")]
47//! #[sibling(("user", Error))]
48//! struct UserData {
49//!     email : String,
50//!     username : String,
51//!     last_login : i64,
52//!     password_hash : String,
53//! }
54//! ```
55//! 
56//! The second part of each relation is a `reindeer::DeletionBehaviour` enum value : `BreakLink`,`Cascade`, or `Error`.
57//! 
58
59mod entity_data;
60mod relations;
61
62use entity_data::EntityData;
63use proc_macro::TokenStream;
64use proc_macro2::Span;
65use syn::{parse_macro_input, DeriveInput, Visibility, spanned::Spanned};
66use quote::quote;
67use syn::Ident;
68
69type Errors = Vec<syn::Error>;
70
71///
72/// Derive macro for `reindeer`'s 🦌 `Entity` trait.
73/// 
74/// To automatically derive Entity on a `struct`, you simply have to derive `Entity` (as Well as `serde`'s `Serialize` and `Deserialize` traits) like so:
75/// 
76/// ```rust
77/// #[derive(Serialize,Deserialize,Entity)]
78/// struct User {
79///     id : (u32,u32),
80///     email : String,
81///     username : String,
82///     last_login : i64,
83///     password_hash : String,
84/// }
85/// ```
86/// 
87/// ☝😉 This will generate an `Entity` implementation with store name `User`, version 0, and id being the `id` field.
88/// To specify other values, use the helper attribute `entity` like so :
89/// 
90/// ```rust
91/// #[derive(Serialize,Deserialize,Entity)]
92/// #[entity(name = "user", version = 1,id = "email")]
93/// struct User {
94///     email : String,
95///     username : String,
96///     last_login : i64,
97///     password_hash : String,
98/// }
99/// ```
100/// 
101/// To specify sibling entities and child entities, use the `sibling` and `child` helper attributes
102/// respectively:
103/// 
104/// ```rust
105/// #[derive(Serialize,Deserialize,Entity)]
106/// #[entity(name = "user", version = 1,id = "email")]
107/// #[sibling(("user_data", Cascade))]
108/// #[children(("doc",Cascade),("shared_doc",BreakLink))]
109/// struct User {
110///     email : String,
111///     username : String,
112///     last_login : i64,
113///     password_hash : String,
114/// }
115/// 
116/// #[derive(Serialize,Deserialize,Entity)]
117/// #[entity(name = "user_data", version = 1,id = "email")]
118/// #[sibling(("user", Error))]
119/// struct UserData {
120///     email : String,
121///     username : String,
122///     last_login : i64,
123///     password_hash : String,
124/// }
125/// ```
126/// 
127/// The second part of each relation is a `reindeer::DeletionBehaviour` enum value : `BreakLink`,`Cascade`, or `Error`.
128/// 
129#[proc_macro_derive(Entity, attributes(entity,children,siblings))]
130pub fn derive_entity(item : TokenStream) -> TokenStream {
131    let ast = parse_macro_input!(item as DeriveInput);
132    let mut errors = Vec::new();
133    let mut result = construct_token_stream(&ast, &mut errors);
134    if errors.len() > 0 {
135        result.extend::<TokenStream>(errors.iter().map(|e| Into::<TokenStream>::into(e.to_compile_error())).collect());
136    }
137    result
138}
139
140fn construct_token_stream(input : &DeriveInput, errors : &mut Errors) -> TokenStream {
141    let mut result = TokenStream::new();
142
143    match &input.data {
144        syn::Data::Struct(s) => {
145            let entity_data = EntityData::parse(&input.span(),&input.attrs,&s.fields, errors);
146            let attr_copy = entity_data.clone();
147            result.extend([
148                generate_alias(&input.ident, entity_data.version.unwrap_or(0), &input.vis, &input.generics),
149                generate_impl( &input.ident, &attr_copy, &input.generics),
150            ])
151        },
152        syn::Data::Enum(_) => errors.push(syn::Error::new_spanned(input, "Cannot derive Entity on an enum. Please implement Entity manually.")),
153        syn::Data::Union(_) => errors.push(syn::Error::new_spanned(input, "Cannot derive Entity on a union. Please implement Entity manually.")),
154    }
155    result
156}
157
158fn generate_alias(name : &Ident,version : u32, vis : &Visibility, generics : &syn::Generics) -> TokenStream {
159    let (_, ty_generics, _) = generics.split_for_impl();
160    let versionned_ident = Ident::new(&format!("{}_v{}",name.to_string(),version), Span::call_site());
161    quote ! {
162        #vis type #versionned_ident #ty_generics = #name #ty_generics;
163    }.into()
164}
165
166fn generate_impl(struct_name : &Ident,entity_data : &EntityData, generics : &syn::Generics) -> TokenStream {
167
168    if let (Some(store_name),Some(id_field),Some(key_type),crate_name) = (&entity_data.name,&entity_data.id,&entity_data.id_type,&entity_data.crate_name) {
169        let crate_name = Ident::new(crate_name,Span::call_site());
170        let children : Vec<proc_macro2::TokenStream> = entity_data.children.0.iter().map(|e| {
171            let (name,deletion) = (e.0.clone(),e.1.clone());
172            quote!{(#name,#crate_name::DeletionBehaviour::#deletion)}
173        }).collect();
174        let siblings: Vec<proc_macro2::TokenStream> = entity_data.siblings.0.iter().map(|e| {
175            let (name,deletion) = (e.0.clone(),e.1.clone());
176            quote!{(#name,#crate_name::DeletionBehaviour::#deletion)}
177        }).collect();
178        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
179        quote!{
180            impl #impl_generics #crate_name::Entity for #struct_name #ty_generics #where_clause {
181                type Key = #key_type;
182                fn store_name() -> &'static str {
183                    #store_name
184                }
185                fn get_key(&self) -> &Self::Key {
186                    &self.#id_field
187                }
188                fn set_key(&mut self, key : &Self::Key) {
189                    self.#id_field = key.clone();
190                }
191                fn get_child_stores() -> Vec<(&'static str, #crate_name::DeletionBehaviour)> {
192                    vec![#(#children)*]
193                }
194                fn get_sibling_stores() -> Vec<(&'static str, #crate_name::DeletionBehaviour)> {
195                    vec![#(#siblings)*]
196                }
197            }
198        }.into()
199    }
200    else {
201        TokenStream::new()
202    }
203}