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}