espr/codegen/rust/
entity.rs

1use crate::ir::*;
2
3use check_keyword::CheckKeyword;
4use inflector::Inflector;
5use proc_macro2::TokenStream;
6use quote::*;
7use syn::parse_quote;
8
9// Each component of Rust struct corresponding to ENTITY in EXPRESS
10struct Field {
11    name: syn::Ident,
12    ty: syn::Type,
13    attributes: Vec<syn::Attribute>,
14}
15
16/// Check if the field should use place holder
17///
18/// A field will not use place holder iff its type is one of:
19///
20/// - a simple type
21/// - an enumeration
22/// - a set or list whose base type use place holder
23///
24fn 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
72// Additional functions to use in codegen/rust for ir::Entity.
73impl 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        // `Any` indentifier must be appears if the entity is supertype
80        assert!(!self.constraints.is_empty());
81        format_ident!("{}Any", self.name.to_pascal_case())
82    }
83
84    /// Field identifier
85    fn field_ident(&self) -> syn::Ident {
86        format_ident!("{}", self.name.as_str().into_safe())
87    }
88
89    /// Generate declaration of `XxxAny` enum
90    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        }); // tokens.append_all
125    }
126
127    /// Generate `impl Into<SelfAny> for SubType` for self and all constraints
128    fn generate_into_any(&self, tokens: &mut TokenStream) {
129        let any = self.any_ident();
130        let name = self.name_ident();
131
132        // `Self` to `SelfAny`
133        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    /// Generate `impl AsRef<Self> for SelfAny` and `impl AsRef<Super> for SelfAny`
156    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        // Each component of struct is called "field" in Rust,
262        // and "attribute" refers other items
263        //
264        // https://doc.rust-lang.org/std/keyword.struct.html
265        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        // Generate `Any` enum if this entity is a supertype of other entities
286        if !self.constraints.is_empty() {
287            self.generate_any_enum(tokens);
288            // Generate `impl Into<XxxAny> for Yyy` for self and all constraints
289            self.generate_into_any(tokens);
290            self.generate_asref_from_any(tokens);
291        }
292    }
293}