ruststep_derive/
lib.rs

1//! Procedural macros for second step code generation
2//!
3//! ```text
4//! ┌────────────────┐
5//! │ EXPRESS schema │
6//! └──┬─────────────┘
7//!    │ esprc
8//! ┌──▼─────────────────┐
9//! │ Abstract Rust code │
10//! └──┬─────────────────┘
11//!    │ ruststep-derive
12//! ┌──▼───────────────┐
13//! │ Actual Rust code │
14//! └──────────────────┘
15//! ```
16//!
17//! Design
18//! -------
19//! - [espr::codegen::rust](../espr/codegen/rust/index.html)
20//!   generates Rust code with proc-macros defined in this crate.
21//! - This crate does not depends on espr explicitly.
22//!
23
24use proc_macro::TokenStream;
25use proc_macro2::TokenStream as TokenStream2;
26use proc_macro_error::{abort_call_site, proc_macro_error};
27use quote::quote;
28use std::convert::*;
29
30mod common;
31mod entity;
32mod field_type;
33mod holder_attr;
34mod select;
35mod table_init;
36mod type_decl;
37
38use common::*;
39use field_type::*;
40use holder_attr::*;
41use table_init::*;
42
43/// Derive `TableInit` for tables
44///
45/// ```
46/// use ruststep_derive::{as_holder, Holder, TableInit};
47/// use std::collections::HashMap;
48///
49/// #[derive(TableInit, Default)]
50/// pub struct Table {
51///     a: HashMap<u64, as_holder!(A)>,
52///     b: HashMap<u64, as_holder!(B)>,
53/// }
54///
55/// #[derive(Debug, Clone, PartialEq, Holder)]
56/// #[holder(table = Table)]
57/// #[holder(field = a)]
58/// #[holder(generate_deserialize)]
59/// pub struct A {
60///     pub x: f64,
61///     pub y: f64,
62/// }
63///
64/// #[derive(Debug, Clone, PartialEq, Holder)]
65/// #[holder(table = Table)]
66/// #[holder(field = b)]
67/// #[holder(generate_deserialize)]
68/// pub struct B {
69///     pub z: f64,
70///     #[holder(use_place_holder)]
71///     pub a: A,
72/// }
73/// ```
74#[proc_macro_error]
75#[proc_macro_derive(TableInit)]
76pub fn derive_table_init_entry(input: TokenStream) -> TokenStream {
77    derive_table_init(&syn::parse(input).unwrap()).into()
78}
79
80/// Generate `impl Deserialize` for entity structs
81#[proc_macro_error]
82#[proc_macro_derive(Deserialize)]
83pub fn derive_deserialize_entry(input: TokenStream) -> TokenStream {
84    derive_deserialize(&syn::parse(input).unwrap()).into()
85}
86
87fn derive_deserialize(ast: &syn::DeriveInput) -> TokenStream2 {
88    let ident = &ast.ident;
89    match &ast.data {
90        syn::Data::Struct(st) => entity::derive_deserialize(ident, st),
91        syn::Data::Enum(e) => select::derive_deserialize(ident, e),
92        _ => abort_call_site!("Only struct is supprted currently"),
93    }
94}
95
96/// Generates `Holder` struct and related implementation for each `ENTITY` struct
97///
98/// `#[holder]` attribute
99/// ---------------------
100///
101/// There are three types of attributes:
102///
103/// ```ignore
104/// #[derive(Holder)]
105/// #[holder(table = Table)] // <- container attribute
106/// #[holder(field = b)]     // <- this is also a container attribute
107/// pub struct B {
108///     pub z: f64,
109///     #[holder(use_place_holder)] // <- field attribute
110///     pub a: A,
111/// }
112///
113/// #[derive(Holder)]
114/// #[holder(table = Table)] // <- container attribute
115/// pub enum S2 {
116///     #[holder(use_place_holder)] // <- this is also a variant attribute
117///     A(Box<A>),
118///     P(f64),
119/// }
120/// ```
121///
122/// - `#[holder(table = {path::to::table::struct})]`
123///   - This must be a container attribute
124///   - Specify a struct path which contains a table for this Holder
125/// - `#[holder(field = {field_ident})]`
126///   - This can be both in container or variant attribute
127///   - Identifier of table field
128/// - `#[holder(generate_deserialize)]`
129///   - This must be a container attribute
130///   - Flag for generating `impl Deserialize for XxxHolder`
131/// - `#[holder(use_place_holder)]`
132///   - This can be both in field or variant attribute
133///   - Specify the field is not a simple type
134///
135#[proc_macro_error]
136#[proc_macro_derive(Holder, attributes(holder))]
137pub fn derive_holder_entry(input: TokenStream) -> TokenStream {
138    derive_holder(&syn::parse(input).unwrap()).into()
139}
140
141fn derive_holder(ast: &syn::DeriveInput) -> TokenStream2 {
142    let attr = HolderAttr::parse(&ast.attrs);
143    let ident = &ast.ident;
144    match &ast.data {
145        syn::Data::Struct(st) => match st.fields {
146            syn::Fields::Named(_) => entity::derive_holder(ident, st, &attr),
147            syn::Fields::Unnamed(_) => type_decl::derive_holder(ident, st, &attr),
148            syn::Fields::Unit => abort_call_site!("Unit struct is not supported."),
149        },
150        syn::Data::Enum(e) => select::derive_holder(ident, e, &attr),
151        _ => abort_call_site!("Only struct is supprted currently"),
152    }
153}
154
155/// Get `Holder` struct identifier from `ENTITY` struct identifier
156///
157/// - e.g. `as_holder!(A)` to `AHolder`
158///
159#[proc_macro_error]
160#[proc_macro]
161pub fn as_holder(input: TokenStream) -> TokenStream {
162    let path = as_holder_path(&syn::parse(input).unwrap());
163    let ts = quote! { #path };
164    ts.into()
165}
166
167#[cfg(test)]
168mod snapshot_tests {
169    use super::derive_holder;
170
171    #[test]
172    fn derive_holder_enum() {
173        let input: syn::DeriveInput = syn::parse_str(
174            r#"
175            #[holder(table = Table)]
176            #[holder(generate_deserialize)]
177            pub enum S1 {
178                #[holder(use_place_holder)]
179                A(Box<A>),
180                #[holder(use_place_holder)]
181                B(Box<B>),
182            }
183            "#,
184        )
185        .unwrap();
186
187        let tt = derive_holder(&input);
188        let out = espr::codegen::rust::rustfmt(tt.to_string());
189
190        insta::assert_snapshot!(out, @r###"
191        #[doc = r" Auto-generated by `#[derive(Holder)]`"]
192        #[derive(Clone, Debug, PartialEq)]
193        pub enum S1Holder {
194            A(Box<AHolder>),
195            B(Box<BHolder>),
196        }
197        impl ::ruststep::tables::IntoOwned for S1Holder {
198            type Owned = S1;
199            type Table = Table;
200            fn into_owned(self, table: &Self::Table) -> ::ruststep::error::Result<Self::Owned> {
201                Ok(match self {
202                    S1Holder::A(sub) => S1::A(Box::new(sub.into_owned(table)?)),
203                    S1Holder::B(sub) => S1::B(Box::new(sub.into_owned(table)?)),
204                })
205            }
206        }
207        impl ::ruststep::tables::Holder for S1Holder {
208            fn name() -> &'static str {
209                "S1"
210            }
211            fn attr_len() -> usize {
212                0
213            }
214        }
215        impl<'de> ::ruststep::serde::de::Deserialize<'de> for S1Holder {
216            fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
217            where
218                D: ::ruststep::serde::de::Deserializer<'de>,
219            {
220                deserializer.deserialize_tuple_struct("S1", 0, S1HolderVisitor {})
221            }
222        }
223        #[doc(hidden)]
224        pub struct S1HolderVisitor;
225        impl<'de> ::ruststep::serde::de::Visitor<'de> for S1HolderVisitor {
226            type Value = S1Holder;
227            fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
228                write!(formatter, "S1")
229            }
230            fn visit_map<A>(self, mut map: A) -> ::std::result::Result<Self::Value, A::Error>
231            where
232                A: ::ruststep::serde::de::MapAccess<'de>,
233            {
234                let key: String = map
235                    .next_key()?
236                    .expect("Empty map cannot be accepted as ruststep Holder");
237                match key.as_str() {
238                    "A" => {
239                        let owned = map.next_value()?;
240                        return Ok(S1Holder::A(Box::new(owned)));
241                    }
242                    "B" => {
243                        let owned = map.next_value()?;
244                        return Ok(S1Holder::B(Box::new(owned)));
245                    }
246                    _ => {
247                        use ruststep::serde::de::{Error, Unexpected};
248                        return Err(A::Error::invalid_value(Unexpected::Other(&key), &self));
249                    }
250                }
251            }
252        }
253        impl ::ruststep::tables::WithVisitor for S1Holder {
254            type Visitor = S1HolderVisitor;
255            fn visitor_new() -> Self::Visitor {
256                S1HolderVisitor {}
257            }
258        }
259        impl ::ruststep::tables::EntityTable<S1Holder> for Table {
260            fn get_owned(&self, entity_id: u64) -> ::ruststep::error::Result<S1> {
261                if let Ok(owned) = ::ruststep::tables::EntityTable::<AHolder>::get_owned(self, entity_id) {
262                    return Ok(S1::A(Box::new(owned.into())));
263                }
264                if let Ok(owned) = ::ruststep::tables::EntityTable::<BHolder>::get_owned(self, entity_id) {
265                    return Ok(S1::B(Box::new(owned.into())));
266                }
267                Err(::ruststep::error::Error::UnknownEntity(entity_id))
268            }
269            fn owned_iter<'table>(
270                &'table self,
271            ) -> Box<dyn Iterator<Item = ::ruststep::error::Result<S1>> + 'table> {
272                Box::new(::ruststep::itertools::chain![
273                    ::ruststep::tables::EntityTable::<AHolder>::owned_iter(self)
274                        .map(|owned| owned.map(|owned| S1::A(Box::new(owned.into())))),
275                    ::ruststep::tables::EntityTable::<BHolder>::owned_iter(self)
276                        .map(|owned| owned.map(|owned| S1::B(Box::new(owned.into()))))
277                ])
278            }
279        }
280        "###);
281    }
282
283    #[test]
284    fn derive_holder_enum_any_subsuper() {
285        let input: syn::DeriveInput = syn::parse_str(
286            r#"
287            # [holder (table = Tables)]
288            #[holder(generate_deserialize)]
289            pub enum BaseAny {
290                #[holder(use_place_holder)]
291                # [holder (field = base)]
292                Base(Box<Base>),
293                #[holder(use_place_holder)]
294                # [holder (field = sub)]
295                Sub(Box<SubAny>),
296            }
297            "#,
298        )
299        .unwrap();
300
301        let tt = derive_holder(&input);
302        let out = espr::codegen::rust::rustfmt(tt.to_string());
303
304        insta::assert_snapshot!(out, @r###"
305        #[doc = r" Auto-generated by `#[derive(Holder)]`"]
306        #[derive(Clone, Debug, PartialEq)]
307        pub enum BaseAnyHolder {
308            Base(Box<BaseHolder>),
309            Sub(Box<SubAnyHolder>),
310        }
311        impl ::ruststep::tables::IntoOwned for BaseAnyHolder {
312            type Owned = BaseAny;
313            type Table = Tables;
314            fn into_owned(self, table: &Self::Table) -> ::ruststep::error::Result<Self::Owned> {
315                Ok(match self {
316                    BaseAnyHolder::Base(sub) => BaseAny::Base(Box::new(sub.into_owned(table)?)),
317                    BaseAnyHolder::Sub(sub) => BaseAny::Sub(Box::new(sub.into_owned(table)?)),
318                })
319            }
320        }
321        impl ::ruststep::tables::Holder for BaseAnyHolder {
322            fn name() -> &'static str {
323                "BASE_ANY"
324            }
325            fn attr_len() -> usize {
326                0
327            }
328        }
329        impl<'de> ::ruststep::serde::de::Deserialize<'de> for BaseAnyHolder {
330            fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
331            where
332                D: ::ruststep::serde::de::Deserializer<'de>,
333            {
334                deserializer.deserialize_tuple_struct("BASE_ANY", 0, BaseAnyHolderVisitor {})
335            }
336        }
337        #[doc(hidden)]
338        pub struct BaseAnyHolderVisitor;
339        impl<'de> ::ruststep::serde::de::Visitor<'de> for BaseAnyHolderVisitor {
340            type Value = BaseAnyHolder;
341            fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
342                write!(formatter, "BASE_ANY")
343            }
344            fn visit_map<A>(self, mut map: A) -> ::std::result::Result<Self::Value, A::Error>
345            where
346                A: ::ruststep::serde::de::MapAccess<'de>,
347            {
348                let key: String = map
349                    .next_key()?
350                    .expect("Empty map cannot be accepted as ruststep Holder");
351                match key.as_str() {
352                    "BASE" => {
353                        let owned = map.next_value()?;
354                        return Ok(BaseAnyHolder::Base(Box::new(owned)));
355                    }
356                    "SUB" => {
357                        let owned = map.next_value()?;
358                        return Ok(BaseAnyHolder::Sub(Box::new(owned)));
359                    }
360                    _ => {
361                        use ruststep::serde::de::{Error, Unexpected};
362                        return Err(A::Error::invalid_value(Unexpected::Other(&key), &self));
363                    }
364                }
365            }
366        }
367        impl ::ruststep::tables::WithVisitor for BaseAnyHolder {
368            type Visitor = BaseAnyHolderVisitor;
369            fn visitor_new() -> Self::Visitor {
370                BaseAnyHolderVisitor {}
371            }
372        }
373        impl ::ruststep::tables::EntityTable<BaseAnyHolder> for Tables {
374            fn get_owned(&self, entity_id: u64) -> ::ruststep::error::Result<BaseAny> {
375                if let Ok(owned) = ::ruststep::tables::EntityTable::<BaseHolder>::get_owned(self, entity_id)
376                {
377                    return Ok(BaseAny::Base(Box::new(owned.into())));
378                }
379                if let Ok(owned) =
380                    ::ruststep::tables::EntityTable::<SubAnyHolder>::get_owned(self, entity_id)
381                {
382                    return Ok(BaseAny::Sub(Box::new(owned.into())));
383                }
384                Err(::ruststep::error::Error::UnknownEntity(entity_id))
385            }
386            fn owned_iter<'table>(
387                &'table self,
388            ) -> Box<dyn Iterator<Item = ::ruststep::error::Result<BaseAny>> + 'table> {
389                Box::new(::ruststep::itertools::chain![
390                    ::ruststep::tables::EntityTable::<BaseHolder>::owned_iter(self)
391                        .map(|owned| owned.map(|owned| BaseAny::Base(Box::new(owned.into())))),
392                    ::ruststep::tables::EntityTable::<SubAnyHolder>::owned_iter(self)
393                        .map(|owned| owned.map(|owned| BaseAny::Sub(Box::new(owned.into()))))
394                ])
395            }
396        }
397        "###);
398    }
399
400    #[test]
401    fn skip_unrelated_attributes() {
402        let input: syn::DeriveInput = syn::parse_str(
403            r#"
404            #[derive(
405                Debug, Clone, PartialEq, AsRef, AsMut, Deref, DerefMut, :: derive_new :: new, Holder,
406            )]
407            # [holder (table = Tables)]
408            # [holder (field = sub1)]
409            #[holder(generate_deserialize)]
410            pub struct Sub1 {
411                #[as_ref]
412                #[as_mut]
413                #[deref]
414                #[deref_mut]
415                #[holder(use_place_holder)]
416                pub base: Base,
417                pub y1: f64,
418            }
419            "#,
420        )
421        .unwrap();
422
423        let tt = derive_holder(&input);
424        let out = espr::codegen::rust::rustfmt(tt.to_string());
425
426        insta::assert_snapshot!(out, @r###"
427        #[doc = r" Auto-generated by `#[derive(Holder)]`"]
428        #[derive(Debug, Clone, PartialEq)]
429        pub struct Sub1Holder {
430            pub base: ::ruststep::tables::PlaceHolder<BaseHolder>,
431            pub y1: f64,
432        }
433        #[automatically_derived]
434        impl ::ruststep::tables::IntoOwned for Sub1Holder {
435            type Table = Tables;
436            type Owned = Sub1;
437            fn into_owned(self, table: &Self::Table) -> ::ruststep::error::Result<Self::Owned> {
438                let Sub1Holder { base, y1 } = self;
439                Ok(Sub1 {
440                    base: base.into_owned(table)?,
441                    y1: y1,
442                })
443            }
444        }
445        #[automatically_derived]
446        impl ::ruststep::tables::Holder for Sub1Holder {
447            fn name() -> &'static str {
448                "SUB_1"
449            }
450            fn attr_len() -> usize {
451                2usize
452            }
453        }
454        #[automatically_derived]
455        impl ::ruststep::tables::EntityTable<Sub1Holder> for Tables {
456            fn get_owned(&self, entity_id: u64) -> ::ruststep::error::Result<Sub1> {
457                ::ruststep::tables::get_owned(self, &self.sub1, entity_id)
458            }
459            fn owned_iter<'table>(
460                &'table self,
461            ) -> Box<dyn Iterator<Item = ::ruststep::error::Result<Sub1>> + 'table> {
462                ::ruststep::tables::owned_iter(self, &self.sub1)
463            }
464        }
465        #[doc(hidden)]
466        pub struct Sub1HolderVisitor;
467        #[automatically_derived]
468        impl<'de> ::ruststep::serde::de::Visitor<'de> for Sub1HolderVisitor {
469            type Value = Sub1Holder;
470            fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
471                write!(formatter, "SUB_1")
472            }
473            fn visit_seq<A>(self, mut seq: A) -> ::std::result::Result<Self::Value, A::Error>
474            where
475                A: ::ruststep::serde::de::SeqAccess<'de>,
476            {
477                if let Some(size) = seq.size_hint() {
478                    if size != 2usize {
479                        use ruststep::serde::de::Error;
480                        return Err(A::Error::invalid_length(size, &self));
481                    }
482                }
483                let base = seq.next_element()?.unwrap();
484                let y1 = seq.next_element()?.unwrap();
485                Ok(Sub1Holder { base, y1 })
486            }
487            fn visit_map<A>(self, mut map: A) -> ::std::result::Result<Self::Value, A::Error>
488            where
489                A: ::ruststep::serde::de::MapAccess<'de>,
490            {
491                let key: String = map
492                    .next_key()?
493                    .expect("Empty map cannot be accepted as ruststep Holder");
494                if key != "SUB_1" {
495                    use ruststep::serde::de::{Error, Unexpected};
496                    return Err(A::Error::invalid_value(Unexpected::Other(&key), &self));
497                }
498                let value = map.next_value()?;
499                Ok(value)
500            }
501        }
502        #[automatically_derived]
503        impl<'de> ::ruststep::serde::de::Deserialize<'de> for Sub1Holder {
504            fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
505            where
506                D: ::ruststep::serde::de::Deserializer<'de>,
507            {
508                deserializer.deserialize_tuple_struct("SUB_1", 2usize, Sub1HolderVisitor {})
509            }
510        }
511        #[automatically_derived]
512        impl ::ruststep::tables::WithVisitor for Sub1Holder {
513            type Visitor = Sub1HolderVisitor;
514            fn visitor_new() -> Self::Visitor {
515                Sub1HolderVisitor {}
516            }
517        }
518        "###);
519    }
520}