Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_reflect/derive/src/trait_reflection.rs
6599 views
1
use bevy_macro_utils::fq_std::{FQClone, FQOption, FQResult};
2
use proc_macro::TokenStream;
3
use quote::quote;
4
use syn::{parse::Parse, parse_macro_input, Attribute, ItemTrait, Token};
5
6
pub(crate) struct TraitInfo {
7
item_trait: ItemTrait,
8
}
9
10
impl Parse for TraitInfo {
11
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
12
let attrs = input.call(Attribute::parse_outer)?;
13
let lookahead = input.lookahead1();
14
if lookahead.peek(Token![pub]) || lookahead.peek(Token![trait]) {
15
let mut item_trait: ItemTrait = input.parse()?;
16
item_trait.attrs = attrs;
17
Ok(TraitInfo { item_trait })
18
} else {
19
Err(lookahead.error())
20
}
21
}
22
}
23
24
/// A trait attribute macro that allows a reflected type to be downcast to a trait object.
25
///
26
/// This generates a struct that takes the form `ReflectMyTrait`. An instance of this struct can then be
27
/// used to perform the conversion.
28
pub(crate) fn reflect_trait(_args: &TokenStream, input: TokenStream) -> TokenStream {
29
let trait_info = parse_macro_input!(input as TraitInfo);
30
let item_trait = &trait_info.item_trait;
31
let trait_ident = &item_trait.ident;
32
let trait_vis = &item_trait.vis;
33
let reflect_trait_ident = crate::ident::get_reflect_ident(&item_trait.ident.to_string());
34
let bevy_reflect_path = crate::meta::get_bevy_reflect_path();
35
36
let struct_doc = format!(
37
" A type generated by the #[reflect_trait] macro for the `{trait_ident}` trait.\n\n This allows casting from `dyn Reflect` to `dyn {trait_ident}`.",
38
);
39
let get_doc = format!(
40
" Downcast a `&dyn Reflect` type to `&dyn {trait_ident}`.\n\n If the type cannot be downcast, `None` is returned.",
41
);
42
let get_mut_doc = format!(
43
" Downcast a `&mut dyn Reflect` type to `&mut dyn {trait_ident}`.\n\n If the type cannot be downcast, `None` is returned.",
44
);
45
let get_box_doc = format!(
46
" Downcast a `Box<dyn Reflect>` type to `Box<dyn {trait_ident}>`.\n\n If the type cannot be downcast, this will return `Err(Box<dyn Reflect>)`.",
47
);
48
49
TokenStream::from(quote! {
50
#item_trait
51
52
#[doc = #struct_doc]
53
#[derive(#FQClone)]
54
#trait_vis struct #reflect_trait_ident {
55
get_func: fn(&dyn #bevy_reflect_path::Reflect) -> #FQOption<&dyn #trait_ident>,
56
get_mut_func: fn(&mut dyn #bevy_reflect_path::Reflect) -> #FQOption<&mut dyn #trait_ident>,
57
get_boxed_func: fn(#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #trait_ident>, #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>>,
58
}
59
60
impl #reflect_trait_ident {
61
#[doc = #get_doc]
62
pub fn get<'a>(&self, reflect_value: &'a dyn #bevy_reflect_path::Reflect) -> #FQOption<&'a dyn #trait_ident> {
63
(self.get_func)(reflect_value)
64
}
65
66
#[doc = #get_mut_doc]
67
pub fn get_mut<'a>(&self, reflect_value: &'a mut dyn #bevy_reflect_path::Reflect) -> #FQOption<&'a mut dyn #trait_ident> {
68
(self.get_mut_func)(reflect_value)
69
}
70
71
#[doc = #get_box_doc]
72
pub fn get_boxed(&self, reflect_value: #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #trait_ident>, #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>> {
73
(self.get_boxed_func)(reflect_value)
74
}
75
}
76
77
impl<T: #trait_ident + #bevy_reflect_path::Reflect> #bevy_reflect_path::FromType<T> for #reflect_trait_ident {
78
fn from_type() -> Self {
79
Self {
80
get_func: |reflect_value| {
81
<dyn #bevy_reflect_path::Reflect>::downcast_ref::<T>(reflect_value).map(|value| value as &dyn #trait_ident)
82
},
83
get_mut_func: |reflect_value| {
84
<dyn #bevy_reflect_path::Reflect>::downcast_mut::<T>(reflect_value).map(|value| value as &mut dyn #trait_ident)
85
},
86
get_boxed_func: |reflect_value| {
87
<dyn #bevy_reflect_path::Reflect>::downcast::<T>(reflect_value).map(|value| value as #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #trait_ident>)
88
}
89
}
90
}
91
}
92
})
93
}
94
95