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}