fyrox_core_derive/
lib.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21#![allow(clippy::manual_unwrap_or_default)]
22
23mod component;
24mod reflect;
25mod uuid;
26mod visit;
27
28use darling::FromDeriveInput;
29use proc_macro::TokenStream;
30use syn::{parse_macro_input, DeriveInput};
31
32/// Implements `Visit` trait
33///
34/// User has to import `Visit`, `Visitor` and `VisitResult` to use this macro.
35///
36/// # Expansion
37///
38/// For example,
39///
40/// ```
41/// use fyrox_core::visitor::{Visit, VisitResult, Visitor};
42/// #[derive(Visit)]
43/// struct Foo<T> {
44///     example_one: String,
45///     example_two: T,
46/// }
47/// # fn main() {}
48/// ```
49///
50/// would expand to something like:
51///
52/// ```
53/// # use fyrox_core::visitor::{Visit, VisitResult, Visitor};
54/// # struct Foo<T> { example_one: String, example_two: T,}
55/// impl<T> Visit for Foo<T> where T: Visit {
56///     fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
57///         let mut region = visitor.enter_region(name)?;
58///         self.example_one.visit("ExampleOne", &mut region)?;
59///         self.example_two.visit("ExampleTwo", &mut region)?;
60///         Ok(())
61///     }
62/// }
63/// # fn main() {}
64/// ```
65///
66/// Field names are converted to strings using
67/// [to_case(Case::UpperCamel)](https://docs.rs/convert_case/0.6.0/convert_case/enum.Case.html#variant.Pascal).
68///
69/// ```
70/// # use fyrox_core::visitor::{Visit, VisitResult, Visitor};
71/// #[derive(Visit)]
72/// struct Pair (usize, usize);
73/// # fn main() {}
74/// ```
75///
76/// would expand to something like:
77///
78/// ```
79/// # use fyrox_core::visitor::{Visit, VisitResult, Visitor};
80/// # struct Pair (usize, usize);
81/// impl Visit for Pair {
82///     fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
83///         let mut region = visitor.enter_region(name)?;
84///         self.0.visit("0", &mut region)?;
85///         self.1.visit("1", &mut region)?;
86///         Ok(())
87///     }
88/// }
89/// # fn main() {}
90/// ```
91///
92/// ```
93/// # use fyrox_core::visitor::{Visit, VisitResult, Visitor};
94/// #[derive(Visit)]
95/// enum EnumExample { A, B(usize) }
96/// # fn main() {}
97/// ```
98///
99/// would expand to something like:
100///
101/// ```
102/// # use fyrox_core::visitor::{Visit, VisitResult, Visitor};
103/// # enum EnumExample { A, B(usize) }
104/// impl Visit for EnumExample {
105///     fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
106///         let mut region = visitor.enter_region(name)?;
107///         let mut id = id(self);
108///         id.visit("Id", &mut region)?;
109///         if region.is_reading() {
110///             *self = from_id(id)?;
111///         }
112///         match self {
113///             EnumExample::A => (),
114///             EnumExample::B(x) => { x.visit("0", &mut region)?; }
115///         }
116///         return Ok(());
117///         fn id(me: &EnumExample) -> u32 {
118///             match me {
119///                 EnumExample::A => 0,
120///                 EnumExample::B(_) => 1,
121///             }
122///         }
123///         fn from_id(id: u32) -> std::result::Result<EnumExample,String> {
124///             match id {
125///                 0 => Ok(EnumExample::A),
126///                 1 => Ok(EnumExample::B(Default::default())),
127///                 _ => Err(format!("Unknown ID for type `EnumExample`: `{}`", id)),
128///             }
129///         }
130///     }
131/// }
132/// # fn main() {}
133/// ```
134#[proc_macro_derive(Visit, attributes(visit))]
135pub fn visit(input: TokenStream) -> TokenStream {
136    let ast = parse_macro_input!(input as DeriveInput);
137    TokenStream::from(visit::impl_visit(ast))
138}
139
140/// Implements `Reflect` trait
141#[proc_macro_derive(Reflect, attributes(reflect))]
142pub fn reflect(input: TokenStream) -> TokenStream {
143    let ast = parse_macro_input!(input as DeriveInput);
144    let mut ty_args = reflect::args::TypeArgs::from_derive_input(&ast).unwrap();
145    ty_args.validate();
146
147    let reflect_impl = reflect::impl_reflect(&ty_args);
148    let prop_key_impl = reflect::impl_prop_constants(&ty_args);
149
150    TokenStream::from(quote::quote! {
151        #reflect_impl
152        #prop_key_impl
153    })
154}
155
156/// Implements `Reflect` by analyzing derive input, without adding property constants
157///
158/// This is used to implement the `Reflect` trait for external types.
159#[proc_macro]
160pub fn impl_reflect(input: TokenStream) -> TokenStream {
161    let ast = parse_macro_input!(input as DeriveInput);
162    let mut ty_args = reflect::args::TypeArgs::from_derive_input(&ast).unwrap();
163    ty_args.validate();
164
165    let reflect_impl = reflect::impl_reflect(&ty_args);
166
167    TokenStream::from(reflect_impl)
168}
169
170#[proc_macro]
171pub fn impl_visit(input: TokenStream) -> TokenStream {
172    let ast = parse_macro_input!(input as DeriveInput);
173    TokenStream::from(visit::impl_visit(ast))
174}
175
176/// Implements `TypeUuidProvider` trait
177///
178/// User has to import `TypeUuidProvider` trait to use this macro.
179#[proc_macro_derive(TypeUuidProvider, attributes(type_uuid))]
180pub fn type_uuid(input: TokenStream) -> TokenStream {
181    let ast = parse_macro_input!(input as DeriveInput);
182    TokenStream::from(uuid::impl_type_uuid_provider(ast))
183}
184
185/// Implements `ComponentProvider` trait
186///
187/// User has to import `ComponentProvider` trait to use this macro.
188#[proc_macro_derive(ComponentProvider, attributes(component))]
189pub fn component(input: TokenStream) -> TokenStream {
190    let ast = parse_macro_input!(input as DeriveInput);
191    TokenStream::from(component::impl_type_uuid_provider(ast))
192}