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 script_message_payload;
26mod uuid;
27mod visit;
28
29use darling::FromDeriveInput;
30use proc_macro::TokenStream;
31use syn::{parse_macro_input, DeriveInput};
32
33/// Implements `Visit` trait
34///
35/// User has to import `Visit`, `Visitor` and `VisitResult` to use this macro.
36///
37/// # Expansion
38///
39/// For example,
40///
41/// ```
42/// use fyrox_core::visitor::{Visit, VisitResult, Visitor};
43/// #[derive(Visit)]
44/// struct Foo<T: Visit> {
45/// example_one: String,
46/// example_two: T,
47/// }
48/// # fn main() {}
49/// ```
50///
51/// would expand to something like:
52///
53/// ```
54/// # use fyrox_core::visitor::{Visit, VisitResult, Visitor};
55/// # struct Foo<T> { example_one: String, example_two: T,}
56/// impl<T> Visit for Foo<T> where T: Visit {
57/// fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
58/// let mut region = visitor.enter_region(name)?;
59/// self.example_one.visit("ExampleOne", &mut region)?;
60/// self.example_two.visit("ExampleTwo", &mut region)?;
61/// Ok(())
62/// }
63/// }
64/// # fn main() {}
65/// ```
66///
67/// Field names are converted to strings using
68/// [to_case(Case::UpperCamel)](https://docs.rs/convert_case/0.6.0/convert_case/enum.Case.html#variant.Pascal).
69///
70/// ```
71/// # use fyrox_core::visitor::{Visit, VisitResult, Visitor};
72/// #[derive(Visit)]
73/// struct Pair (usize, usize);
74/// # fn main() {}
75/// ```
76///
77/// would expand to something like:
78///
79/// ```
80/// # use fyrox_core::visitor::{Visit, VisitResult, Visitor};
81/// # struct Pair (usize, usize);
82/// impl Visit for Pair {
83/// fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
84/// let mut region = visitor.enter_region(name)?;
85/// self.0.visit("0", &mut region)?;
86/// self.1.visit("1", &mut region)?;
87/// Ok(())
88/// }
89/// }
90/// # fn main() {}
91/// ```
92///
93/// ```
94/// # use fyrox_core::visitor::{Visit, VisitResult, Visitor};
95/// #[derive(Visit)]
96/// enum EnumExample { A, B(usize) }
97/// # fn main() {}
98/// ```
99///
100/// would expand to something like:
101///
102/// ```
103/// # use fyrox_core::visitor::{Visit, VisitResult, Visitor};
104/// # enum EnumExample { A, B(usize) }
105/// impl Visit for EnumExample {
106/// fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
107/// let mut region = visitor.enter_region(name)?;
108/// let mut id = id(self);
109/// id.visit("Id", &mut region)?;
110/// if region.is_reading() {
111/// *self = from_id(id)?;
112/// }
113/// match self {
114/// EnumExample::A => (),
115/// EnumExample::B(x) => { x.visit("0", &mut region)?; }
116/// }
117/// return Ok(());
118/// fn id(me: &EnumExample) -> u32 {
119/// match me {
120/// EnumExample::A => 0,
121/// EnumExample::B(_) => 1,
122/// }
123/// }
124/// fn from_id(id: u32) -> std::result::Result<EnumExample,String> {
125/// match id {
126/// 0 => Ok(EnumExample::A),
127/// 1 => Ok(EnumExample::B(Default::default())),
128/// _ => Err(format!("Unknown ID for type `EnumExample`: `{}`", id)),
129/// }
130/// }
131/// }
132/// }
133/// # fn main() {}
134/// ```
135#[proc_macro_derive(Visit, attributes(visit))]
136pub fn visit(input: TokenStream) -> TokenStream {
137 let ast = parse_macro_input!(input as DeriveInput);
138 TokenStream::from(visit::impl_visit(ast))
139}
140
141/// Implements `Reflect` trait
142#[proc_macro_derive(Reflect, attributes(reflect))]
143pub fn reflect(input: TokenStream) -> TokenStream {
144 let ast = parse_macro_input!(input as DeriveInput);
145 let mut ty_args = reflect::args::TypeArgs::from_derive_input(&ast).unwrap();
146 ty_args.validate();
147
148 let reflect_impl = reflect::impl_reflect(&ty_args);
149 let prop_key_impl = reflect::impl_prop_constants(&ty_args);
150
151 TokenStream::from(quote::quote! {
152 #reflect_impl
153 #prop_key_impl
154 })
155}
156
157/// Implements `Reflect` by analyzing derive input, without adding property constants
158///
159/// This is used to implement the `Reflect` trait for external types.
160#[proc_macro]
161pub fn impl_reflect(input: TokenStream) -> TokenStream {
162 let ast = parse_macro_input!(input as DeriveInput);
163 let mut ty_args = reflect::args::TypeArgs::from_derive_input(&ast).unwrap();
164 ty_args.validate();
165
166 let reflect_impl = reflect::impl_reflect(&ty_args);
167
168 TokenStream::from(reflect_impl)
169}
170
171#[proc_macro]
172pub fn impl_visit(input: TokenStream) -> TokenStream {
173 let ast = parse_macro_input!(input as DeriveInput);
174 TokenStream::from(visit::impl_visit(ast))
175}
176
177/// Implements `TypeUuidProvider` trait
178///
179/// User has to import `TypeUuidProvider` trait to use this macro.
180#[proc_macro_derive(TypeUuidProvider, attributes(type_uuid))]
181pub fn type_uuid(input: TokenStream) -> TokenStream {
182 let ast = parse_macro_input!(input as DeriveInput);
183 TokenStream::from(uuid::impl_type_uuid_provider(ast))
184}
185
186/// Implements `ComponentProvider` trait
187///
188/// User has to import `ComponentProvider` trait to use this macro.
189#[proc_macro_derive(ComponentProvider, attributes(component))]
190pub fn component(input: TokenStream) -> TokenStream {
191 let ast = parse_macro_input!(input as DeriveInput);
192 TokenStream::from(component::impl_type_uuid_provider(ast))
193}
194
195/// Implements `ScriptMessagePayload` trait
196///
197/// User has to import `ScriptMessagePayload` trait to use this macro.
198#[proc_macro_derive(ScriptMessagePayload)]
199pub fn script_message_payload(input: TokenStream) -> TokenStream {
200 let ast = parse_macro_input!(input as DeriveInput);
201 TokenStream::from(script_message_payload::impl_script_message_payload(ast))
202}