espr/codegen/rust/
entity.rs1use crate::ir::*;
2
3use check_keyword::CheckKeyword;
4use inflector::Inflector;
5use proc_macro2::TokenStream;
6use quote::*;
7use syn::parse_quote;
8
9struct Field {
11 name: syn::Ident,
12 ty: syn::Type,
13 attributes: Vec<syn::Attribute>,
14}
15
16fn use_place_holder(ty: &TypeRef) -> bool {
25 match ty {
26 TypeRef::SimpleType(..) => false,
27 TypeRef::Named { is_enumerate, .. } => !*is_enumerate,
28 TypeRef::Set { base, .. } | TypeRef::List { base, .. } => use_place_holder(base),
29 _ => true,
30 }
31}
32
33impl From<EntityAttribute> for Field {
34 fn from(attr: EntityAttribute) -> Self {
35 let EntityAttribute { name, ty, optional } = attr;
36
37 let name = format_ident!("{}", name.into_safe());
38 let attributes = if use_place_holder(&ty) {
39 vec![parse_quote! { #[holder(use_place_holder)] }]
40 } else {
41 Vec::new()
42 };
43 let ty = if optional {
44 parse_quote! { Option<#ty> }
45 } else {
46 parse_quote! { #ty }
47 };
48
49 Field {
50 name,
51 ty,
52 attributes,
53 }
54 }
55}
56
57impl ToTokens for Field {
58 fn to_tokens(&self, tokens: &mut TokenStream) {
59 let Field {
60 name,
61 ty,
62 attributes,
63 } = self;
64
65 tokens.append_all(quote! {
66 #( #attributes )*
67 pub #name : #ty
68 });
69 }
70}
71
72impl Entity {
74 fn name_ident(&self) -> syn::Ident {
75 format_ident!("{}", self.name.to_pascal_case())
76 }
77
78 fn any_ident(&self) -> syn::Ident {
79 assert!(!self.constraints.is_empty());
81 format_ident!("{}Any", self.name.to_pascal_case())
82 }
83
84 fn field_ident(&self) -> syn::Ident {
86 format_ident!("{}", self.name.as_str().into_safe())
87 }
88
89 fn generate_any_enum(&self, tokens: &mut TokenStream) {
91 let any = self.any_ident();
92
93 let mut fields = vec![format_ident!("{}", self.name.as_str().into_safe())];
94 let mut variants = vec![format_ident!("{}", self.name.to_pascal_case())];
95 let mut constraints = vec![format_ident!("{}", self.name.to_pascal_case())];
96
97 for ty in &self.constraints {
98 match ty {
99 TypeRef::Entity {
100 name, is_supertype, ..
101 } => {
102 fields.push(format_ident!("{}", name.as_str().into_safe()));
103 variants.push(format_ident!("{}", name.to_pascal_case()));
104 if *is_supertype {
105 constraints.push(format_ident!("{}Any", name.to_pascal_case()));
106 } else {
107 constraints.push(format_ident!("{}", name.to_pascal_case()));
108 }
109 }
110 _ => unreachable!(),
111 }
112 }
113
114 tokens.append_all(quote! {
115 #[derive(Debug, Clone, PartialEq, Holder)]
116 #[holder(table = Tables)]
117 #[holder(generate_deserialize)]
118 pub enum #any {
119 #(
120 #[holder(use_place_holder)]
121 #variants(Box<#constraints>)
122 ),*
123 }
124 }); }
126
127 fn generate_into_any(&self, tokens: &mut TokenStream) {
129 let any = self.any_ident();
130 let name = self.name_ident();
131
132 tokens.append_all(quote! {
134 impl Into<#any> for #name {
135 fn into(self) -> #any {
136 #any::#name(Box::new(self))
137 }
138 }
139 });
140
141 for ty in &self.constraints {
142 if let TypeRef::Entity { name, .. } = ty {
143 let name = format_ident!("{}", name.to_pascal_case());
144 tokens.append_all(quote! {
145 impl Into<#any> for #name {
146 fn into(self) -> #any {
147 #any::#name(Box::new(self.into()))
148 }
149 }
150 });
151 }
152 }
153 }
154
155 fn generate_asref_from_any(&self, tokens: &mut TokenStream) {
157 let any = self.any_ident();
158 let name = self.name_ident();
159
160 let constraints = self
161 .constraints
162 .iter()
163 .map(|ty| match ty {
164 TypeRef::Entity { name, .. } => {
165 format_ident!("{}", name.to_pascal_case())
166 }
167 _ => unreachable!(),
168 })
169 .collect::<Vec<_>>();
170
171 tokens.append_all(quote! {
172 impl AsRef<#name> for #any {
173 fn as_ref(&self) -> &#name {
174 match self {
175 #any::#name (x) => x.as_ref(),
176 #(#any::#constraints (x) => (**x).as_ref(),)*
177 }
178 }
179 }
180 });
181
182 for ty in &self.supertypes {
183 let supertype = match ty {
184 TypeRef::Entity { name, .. } => {
185 format_ident!("{}", name.to_pascal_case())
186 }
187 _ => unreachable!(),
188 };
189
190 tokens.append_all(quote! {
191 impl AsRef<#supertype> for #any {
192 fn as_ref(&self) -> &#supertype {
193 match self {
194 #any::#name (x) => AsRef::<#name>::as_ref(x).as_ref(),
195 #(#any::#constraints (x) => AsRef::<#name>::as_ref(x.as_ref()).as_ref(),)*
196 }
197 }
198 }
199 });
200 }
201 }
202
203 fn supertype_fields(&self) -> Vec<Field> {
204 self.supertypes
205 .iter()
206 .map(|ty| {
207 let mut attributes = Vec::new();
208 attributes.push(parse_quote! { #[as_ref] });
209 attributes.push(parse_quote! { #[as_mut] });
210
211 if self.supertypes.len() == 1 {
212 attributes.push(parse_quote! { #[deref] });
213 attributes.push(parse_quote! { #[deref_mut] });
214 }
215 attributes.push(parse_quote! { #[holder(use_place_holder)] });
216 let (name, ty) = match ty {
217 TypeRef::Named { name, .. } | TypeRef::Entity { name, .. } => {
218 let ty = format_ident!("{}", name.to_pascal_case());
219 (
220 format_ident!("{}", name.as_str().into_safe()),
221 parse_quote! { #ty },
222 )
223 }
224 _ => unreachable!(),
225 };
226
227 Field {
228 name,
229 ty,
230 attributes,
231 }
232 })
233 .collect()
234 }
235
236 fn derives(&self) -> Vec<syn::Path> {
237 let mut derives = vec![
238 syn::parse_str("Debug").unwrap(),
239 syn::parse_str("Clone").unwrap(),
240 syn::parse_str("PartialEq").unwrap(),
241 syn::parse_str("::derive_new::new").unwrap(),
242 syn::parse_str("Holder").unwrap(),
243 ];
244 if !self.supertypes.is_empty() {
245 derives.push(syn::parse_str("AsRef").unwrap());
246 derives.push(syn::parse_str("AsMut").unwrap());
247 }
248 if self.supertypes.len() == 1 {
249 derives.push(syn::parse_str("Deref").unwrap());
250 derives.push(syn::parse_str("DerefMut").unwrap());
251 }
252 derives
253 }
254}
255
256impl ToTokens for Entity {
257 fn to_tokens(&self, tokens: &mut TokenStream) {
258 let name = self.name_ident();
259 let field_name = self.field_ident();
260
261 let fields = self
266 .attributes
267 .iter()
268 .map(|attr| Field::from(attr.clone()))
269 .collect::<Vec<Field>>();
270 let supertype_fields = self.supertype_fields();
271
272 let derive = self.derives();
273
274 tokens.append_all(quote! {
275 #( #[derive(#derive)] )*
276 #[holder(table = Tables)]
277 #[holder(field = #field_name)]
278 #[holder(generate_deserialize)]
279 pub struct #name {
280 #(#supertype_fields,)*
281 #(#fields,)*
282 }
283 });
284
285 if !self.constraints.is_empty() {
287 self.generate_any_enum(tokens);
288 self.generate_into_any(tokens);
290 self.generate_asref_from_any(tokens);
291 }
292 }
293}