Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bevy_reflect: Trait casting #5001

Closed
wants to merge 15 commits into from
2 changes: 1 addition & 1 deletion crates/bevy_asset/src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use serde::{Deserialize, Serialize};
Reflect,
FromReflect,
)]
#[reflect_value(Serialize, Deserialize, PartialEq, Hash)]
#[reflect_value(Deserialize, PartialEq, Hash)]
pub enum HandleId {
Id(Uuid, u64),
AssetPathId(AssetPathId),
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_asset/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,19 @@ impl<'a> AssetPath<'a> {
#[derive(
Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Reflect,
)]
#[reflect_value(PartialEq, Hash, Serialize, Deserialize)]
#[reflect_value(PartialEq, Hash, Deserialize)]
pub struct AssetPathId(SourcePathId, LabelId);

#[derive(
Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Reflect,
)]
#[reflect_value(PartialEq, Hash, Serialize, Deserialize)]
#[reflect_value(PartialEq, Hash, Deserialize)]
pub struct SourcePathId(u64);

#[derive(
Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Reflect,
)]
#[reflect_value(PartialEq, Hash, Serialize, Deserialize)]
#[reflect_value(PartialEq, Hash, Deserialize)]
pub struct LabelId(u64);

impl<'a> From<&'a Path> for SourcePathId {
Expand Down
63 changes: 41 additions & 22 deletions crates/bevy_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ pub mod prelude {

use bevy_app::prelude::*;
use bevy_ecs::entity::Entity;
use bevy_utils::HashSet;
use std::ops::Range;
use bevy_reflect::TypeRegistryArc;

/// Adds core functionality to Apps.
#[derive(Default)]
Expand All @@ -34,29 +33,49 @@ impl Plugin for CorePlugin {

app.register_type::<Entity>().register_type::<Name>();

register_rust_types(app);
register_math_types(app);
let registry = app.world.resource::<TypeRegistryArc>();
let mut registry = registry.write();
rust_types::register_types(&mut registry);
math_types::register_types(&mut registry);
}
}

fn register_rust_types(app: &mut App) {
app.register_type::<Range<f32>>()
.register_type::<String>()
.register_type::<HashSet<String>>()
.register_type::<Option<String>>();
mod rust_types {
use bevy_reflect::erased_serde::Serialize;
use bevy_reflect::register_all;
use bevy_utils::HashSet;
use std::ops::Range;

register_all! {
traits: [Serialize],
types: [
String,
Option<String>,
Range<f32>,
HashSet<String>
]
}
}

fn register_math_types(app: &mut App) {
app.register_type::<bevy_math::IVec2>()
.register_type::<bevy_math::IVec3>()
.register_type::<bevy_math::IVec4>()
.register_type::<bevy_math::UVec2>()
.register_type::<bevy_math::UVec3>()
.register_type::<bevy_math::UVec4>()
.register_type::<bevy_math::Vec2>()
.register_type::<bevy_math::Vec3>()
.register_type::<bevy_math::Vec4>()
.register_type::<bevy_math::Mat3>()
.register_type::<bevy_math::Mat4>()
.register_type::<bevy_math::Quat>();
mod math_types {
use bevy_reflect::erased_serde::Serialize;
use bevy_reflect::register_all;

register_all! {
traits: [Serialize],
types: [
bevy_math::IVec2,
bevy_math::IVec3,
bevy_math::IVec4,
bevy_math::UVec2,
bevy_math::UVec3,
bevy_math::UVec4,
bevy_math::Vec2,
bevy_math::Vec3,
bevy_math::Vec4,
bevy_math::Mat3,
bevy_math::Mat4,
bevy_math::Quat,
]
}
}
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/reflect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
}
}

impl_reflect_value!(Entity(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(Entity(Hash, PartialEq, Deserialize));
impl_from_reflect_value!(Entity);

#[derive(Clone)]
Expand Down
28 changes: 28 additions & 0 deletions crates/bevy_reflect/bevy_reflect_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,31 @@ pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream {
let ty = &reflect_value_def.type_name;
from_reflect::impl_value(ty, &reflect_value_def.generics, &bevy_reflect_path)
}

/// A macro that allows for mass type and trait registration.
///
/// This will generate a public function with the signature: `fn register_types(&mut TypeRegistry)`.
/// You can then use this generated function to register the given types and traits.
///
/// # Example
///
/// ```
/// use bevy_reflect_derive::register_all;
///
/// trait MyTrait {}
/// struct MyType;
/// struct MyOtherType;
///
/// impl MyTrait for MyType {}
///
/// // Not all types need to implement all traits
/// register_all! {
/// traits: [MyTrait],
/// types: [MyType, MyOtherType],
/// }
///
/// ```
#[proc_macro]
pub fn register_all(item: TokenStream) -> TokenStream {
registration::register_all_internal(item)
}
112 changes: 112 additions & 0 deletions crates/bevy_reflect/bevy_reflect_derive/src/registration.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
//! Contains code related specifically to Bevy's type registration.

use bevy_macro_utils::BevyManifest;
use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::{Iter, Punctuated};
use syn::{bracketed, parse_macro_input, Token, Type};
use syn::{Generics, Path};

/// Creates the `GetTypeRegistration` impl for the given type data.
Expand All @@ -23,3 +28,110 @@ pub(crate) fn impl_get_type_registration(
}
}
}

pub fn register_all_internal(item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as RegisterAllData);

let registration_params = input.types().map(|ty| {
let trait_type = input.traits();
quote! {
#ty, #(#trait_type),*
}
});

let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect");

TokenStream::from(quote! {
pub fn register_types(registry: &mut #bevy_reflect_path::TypeRegistry) {
#(#bevy_reflect_path::register_type!(registry, #registration_params));*
}
})
}

/// Maps to the following invocation:
///
/// ```
/// use bevy_reflect_derive::register_all;
///
/// trait MyTrait {}
/// struct MyType {}
///
/// register_all! {
/// traits: [MyTrait],
/// types: [MyType],
/// }
/// ```
///
/// > Note: The order of the `traits` and `types` fields does not matter. Additionally,
/// > the commas (separating and trailing) may be omitted entirely.
struct RegisterAllData {
trait_list: Punctuated<Type, Token![,]>,
type_list: Punctuated<Type, Token![,]>,
}

impl RegisterAllData {
/// Returns an iterator over the types to register.
fn types(&self) -> Iter<Type> {
self.type_list.iter()
}

/// Returns an iterator over the traits to register.
fn traits(&self) -> Iter<Type> {
self.trait_list.iter()
}

/// Parse a list of types.
///
/// This is the portion _after_ the the respective keyword and consumes: `: [ Foo, Bar, Baz ]`
fn parse_list(input: &mut ParseStream) -> syn::Result<Punctuated<Type, Token![,]>> {
input.parse::<Token![:]>()?;
let list;
bracketed!(list in input);
list.parse_terminated(Type::parse)
}
}

impl Parse for RegisterAllData {
fn parse(mut input: ParseStream) -> syn::Result<Self> {
let trait_list;
let type_list;

let lookahead = input.lookahead1();
if lookahead.peek(kw::traits) {
// Parse `traits` then `types`
input.parse::<kw::traits>()?;
trait_list = Self::parse_list(&mut input)?;
// Optional separating comma
input.parse::<Option<Token![,]>>()?;
input.parse::<kw::types>()?;
type_list = Self::parse_list(&mut input)?;
// Optional trailing comma
input.parse::<Option<Token![,]>>()?;
} else if lookahead.peek(kw::types) {
// Parse `types` then `traits`
input.parse::<kw::types>()?;
type_list = Self::parse_list(&mut input)?;
// Optional separating comma
input.parse::<Option<Token![,]>>()?;
input.parse::<kw::traits>()?;
trait_list = Self::parse_list(&mut input)?;
// Optional trailing comma
input.parse::<Option<Token![,]>>()?;
} else {
return Err(syn::Error::new(
input.span(),
"expected either 'traits' or 'types' field",
));
}

Ok(Self {
trait_list,
type_list,
})
}
}

mod kw {
syn::custom_keyword!(traits);
syn::custom_keyword!(types);
}
Loading