From d3e0196cbbb38510fd7d08cd6982e13a16af11f9 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sun, 24 May 2020 11:01:48 -0700 Subject: [PATCH] move derive(Properties) into bevy_properties --- crates/bevy_derive/src/lib.rs | 131 ---------------- crates/bevy_derive/src/modules.rs | 10 -- crates/bevy_property/Cargo.toml | 3 +- .../bevy_property_derive/Cargo.toml | 17 +++ .../bevy_property_derive/src/lib.rs | 141 ++++++++++++++++++ .../bevy_property_derive/src/modules.rs | 84 +++++++++++ crates/bevy_property/src/lib.rs | 1 + 7 files changed, 245 insertions(+), 142 deletions(-) create mode 100644 crates/bevy_property/bevy_property_derive/Cargo.toml create mode 100644 crates/bevy_property/bevy_property_derive/src/lib.rs create mode 100644 crates/bevy_property/bevy_property_derive/src/modules.rs diff --git a/crates/bevy_derive/src/lib.rs b/crates/bevy_derive/src/lib.rs index 2ab2fbf1ef..40d00b74f6 100644 --- a/crates/bevy_derive/src/lib.rs +++ b/crates/bevy_derive/src/lib.rs @@ -50,137 +50,6 @@ pub fn derive_resource(input: TokenStream) -> TokenStream { }) } -#[proc_macro_derive(Properties, attributes(prop, module))] -pub fn derive_props(input: TokenStream) -> TokenStream { - let ast = parse_macro_input!(input as DeriveInput); - let fields = match &ast.data { - Data::Struct(DataStruct { - fields: Fields::Named(fields), - .. - }) => &fields.named, - _ => panic!("expected a struct with named fields"), - }; - - let modules = get_modules(&ast); - let bevy_property_path = get_path(&modules.bevy_property); - - let field_names = fields.iter().map(|field| field.ident - .as_ref() - .unwrap() - .to_string()).collect::>(); - let field_idents = fields.iter().map(|field| field.ident.as_ref().unwrap()).collect::>(); - let field_count = fields.len(); - let field_indices = (0..field_count).collect::>(); - - - let generics = ast.generics; - let (impl_generics, ty_generics, _where_clause) = generics.split_for_impl(); - - let struct_name = &ast.ident; - - TokenStream::from(quote! { - impl #impl_generics #bevy_property_path::Properties for #struct_name#ty_generics { - fn type_name(&self) -> &str { - std::any::type_name::() - } - fn prop(&self, name: &str) -> Option<&dyn #bevy_property_path::Property> { - match name { - #(#field_names => Some(&self.#field_idents),)* - _ => None, - } - } - - fn prop_mut(&mut self, name: &str) -> Option<&mut dyn #bevy_property_path::Property> { - match name { - #(#field_names => Some(&mut self.#field_idents),)* - _ => None, - } - } - - fn prop_with_index(&self, index: usize) -> Option<&dyn #bevy_property_path::Property> { - match index { - #(#field_indices => Some(&self.#field_idents),)* - _ => None, - } - } - - fn prop_with_index_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_property_path::Property> { - match index { - #(#field_indices => Some(&mut self.#field_idents),)* - _ => None, - } - } - - fn prop_name(&self, index: usize) -> Option<&str> { - match index { - #(#field_indices => Some(#field_names),)* - _ => None, - } - } - - fn prop_len(&self) -> usize { - #field_count - } - - fn iter_props(&self) -> #bevy_property_path::PropertyIter { - #bevy_property_path::PropertyIter::new(self) - } - } - - impl #impl_generics #bevy_property_path::serde::ser::Serialize for #struct_name#ty_generics { - fn serialize(&self, serializer: S) -> Result - where - S: #bevy_property_path::serde::ser::Serializer, - { - use #bevy_property_path::serde::ser::SerializeMap; - let mut state = serializer.serialize_map(Some(self.prop_len()))?; - state.serialize_entry("type", self.type_name())?; - for (name, prop) in self.iter_props() { - state.serialize_entry(name, prop)?; - } - state.end() - } - } - - impl #impl_generics #bevy_property_path::Property for #struct_name#ty_generics { - #[inline] - fn any(&self) -> &dyn std::any::Any { - self - } - #[inline] - fn any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - #[inline] - fn clone_prop(&self) -> Box { - Box::new(self.to_dynamic()) - } - #[inline] - fn set(&mut self, value: &dyn #bevy_property_path::Property) { - // TODO: type check - self.apply(value); - } - - #[inline] - fn apply(&mut self, value: &dyn #bevy_property_path::Property) { - if let Some(properties) = value.as_properties() { - for (name, prop) in properties.iter_props() { - self.prop_mut(name).map(|p| p.apply(prop)); - } - } else { - panic!("attempted to apply non-Properties type to Properties type"); - } - } - } - - impl #impl_generics #bevy_property_path::AsProperties for #struct_name#ty_generics { - fn as_properties(&self) -> Option<&dyn #bevy_property_path::Properties> { - Some(self) - } - } - }) -} - #[proc_macro_derive(Uniform, attributes(uniform, module))] pub fn derive_uniform(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); diff --git a/crates/bevy_derive/src/modules.rs b/crates/bevy_derive/src/modules.rs index 1a2d27be32..275976daec 100644 --- a/crates/bevy_derive/src/modules.rs +++ b/crates/bevy_derive/src/modules.rs @@ -11,8 +11,6 @@ pub struct ModuleAttributeArgs { #[darling(default)] pub bevy_core: Option, #[darling(default)] - pub bevy_property: Option, - #[darling(default)] pub bevy_app: Option, #[darling(default)] pub legion: Option, @@ -27,7 +25,6 @@ pub struct Modules { pub bevy_render: String, pub bevy_asset: String, pub bevy_core: String, - pub bevy_property: String, pub bevy_app: String, pub legion: String, } @@ -38,7 +35,6 @@ impl Modules { bevy_asset: "bevy::asset".to_string(), bevy_render: "bevy::render".to_string(), bevy_core: "bevy::core".to_string(), - bevy_property: "bevy::property".to_string(), bevy_app: "bevy::app".to_string(), legion: "bevy".to_string(), } @@ -49,7 +45,6 @@ impl Modules { bevy_asset: "bevy_asset".to_string(), bevy_render: "bevy_render".to_string(), bevy_core: "bevy_core".to_string(), - bevy_property: "bevy_property".to_string(), bevy_app: "bevy_app".to_string(), legion: "legion".to_string(), } @@ -62,7 +57,6 @@ impl Default for ModuleAttributeArgs { bevy_asset: None, bevy_render: None, bevy_core: None, - bevy_property: None, bevy_app: None, legion: None, meta: true, @@ -96,10 +90,6 @@ pub fn get_modules(ast: &DeriveInput) -> Modules { modules.bevy_render = path; } - if let Some(path) = module_attribute_args.bevy_property { - modules.bevy_property = path; - } - if let Some(path) = module_attribute_args.bevy_core { modules.bevy_core = path; } diff --git a/crates/bevy_property/Cargo.toml b/crates/bevy_property/Cargo.toml index a6c49658a2..499ea184cc 100644 --- a/crates/bevy_property/Cargo.toml +++ b/crates/bevy_property/Cargo.toml @@ -8,4 +8,5 @@ edition = "2018" [dependencies] serde = "1" -erased-serde = "0.3" \ No newline at end of file +erased-serde = "0.3" +bevy_property_derive = { path = "bevy_property_derive" } \ No newline at end of file diff --git a/crates/bevy_property/bevy_property_derive/Cargo.toml b/crates/bevy_property/bevy_property_derive/Cargo.toml new file mode 100644 index 0000000000..d06c6e646e --- /dev/null +++ b/crates/bevy_property/bevy_property_derive/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "bevy_property_derive" +version = "0.1.0" +authors = ["Carter Anderson "] +edition = "2018" + +[features] +default_bevy_meta = [] + +[lib] +proc-macro = true + +[dependencies] +syn = "1.0" +proc-macro2 = "1.0" +quote = "1.0" +darling = "0.10.2" \ No newline at end of file diff --git a/crates/bevy_property/bevy_property_derive/src/lib.rs b/crates/bevy_property/bevy_property_derive/src/lib.rs new file mode 100644 index 0000000000..bafbe93ad5 --- /dev/null +++ b/crates/bevy_property/bevy_property_derive/src/lib.rs @@ -0,0 +1,141 @@ +extern crate proc_macro; + +mod modules; + +use modules::{get_modules, get_path}; +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields}; + +#[proc_macro_derive(Properties, attributes(prop, module))] +pub fn derive_properties(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + let fields = match &ast.data { + Data::Struct(DataStruct { + fields: Fields::Named(fields), + .. + }) => &fields.named, + _ => panic!("expected a struct with named fields"), + }; + + let modules = get_modules(&ast); + let bevy_property_path = get_path(&modules.bevy_property); + + let field_names = fields + .iter() + .map(|field| field.ident.as_ref().unwrap().to_string()) + .collect::>(); + let field_idents = fields + .iter() + .map(|field| field.ident.as_ref().unwrap()) + .collect::>(); + let field_count = fields.len(); + let field_indices = (0..field_count).collect::>(); + + let generics = ast.generics; + let (impl_generics, ty_generics, _where_clause) = generics.split_for_impl(); + + let struct_name = &ast.ident; + + TokenStream::from(quote! { + impl #impl_generics #bevy_property_path::Properties for #struct_name#ty_generics { + fn type_name(&self) -> &str { + std::any::type_name::() + } + fn prop(&self, name: &str) -> Option<&dyn #bevy_property_path::Property> { + match name { + #(#field_names => Some(&self.#field_idents),)* + _ => None, + } + } + + fn prop_mut(&mut self, name: &str) -> Option<&mut dyn #bevy_property_path::Property> { + match name { + #(#field_names => Some(&mut self.#field_idents),)* + _ => None, + } + } + + fn prop_with_index(&self, index: usize) -> Option<&dyn #bevy_property_path::Property> { + match index { + #(#field_indices => Some(&self.#field_idents),)* + _ => None, + } + } + + fn prop_with_index_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_property_path::Property> { + match index { + #(#field_indices => Some(&mut self.#field_idents),)* + _ => None, + } + } + + fn prop_name(&self, index: usize) -> Option<&str> { + match index { + #(#field_indices => Some(#field_names),)* + _ => None, + } + } + + fn prop_len(&self) -> usize { + #field_count + } + + fn iter_props(&self) -> #bevy_property_path::PropertyIter { + #bevy_property_path::PropertyIter::new(self) + } + } + + impl #impl_generics #bevy_property_path::serde::ser::Serialize for #struct_name#ty_generics { + fn serialize(&self, serializer: S) -> Result + where + S: #bevy_property_path::serde::ser::Serializer, + { + use #bevy_property_path::serde::ser::SerializeMap; + let mut state = serializer.serialize_map(Some(self.prop_len()))?; + state.serialize_entry("type", self.type_name())?; + for (name, prop) in self.iter_props() { + state.serialize_entry(name, prop)?; + } + state.end() + } + } + + impl #impl_generics #bevy_property_path::Property for #struct_name#ty_generics { + #[inline] + fn any(&self) -> &dyn std::any::Any { + self + } + #[inline] + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + #[inline] + fn clone_prop(&self) -> Box { + Box::new(self.to_dynamic()) + } + #[inline] + fn set(&mut self, value: &dyn #bevy_property_path::Property) { + // TODO: type check + self.apply(value); + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_property_path::Property) { + if let Some(properties) = value.as_properties() { + for (name, prop) in properties.iter_props() { + self.prop_mut(name).map(|p| p.apply(prop)); + } + } else { + panic!("attempted to apply non-Properties type to Properties type"); + } + } + } + + impl #impl_generics #bevy_property_path::AsProperties for #struct_name#ty_generics { + fn as_properties(&self) -> Option<&dyn #bevy_property_path::Properties> { + Some(self) + } + } + }) +} diff --git a/crates/bevy_property/bevy_property_derive/src/modules.rs b/crates/bevy_property/bevy_property_derive/src/modules.rs new file mode 100644 index 0000000000..178cdebf6f --- /dev/null +++ b/crates/bevy_property/bevy_property_derive/src/modules.rs @@ -0,0 +1,84 @@ +use darling::FromMeta; +use proc_macro::TokenStream; +use syn::{DeriveInput, Path}; + +#[derive(FromMeta, Debug)] +pub struct ModuleAttributeArgs { + #[darling(default)] + pub bevy_property: Option, + /// If true, it will use the meta "bevy" crate for dependencies by default (ex: bevy:app). If this is set to false, the individual bevy crates + /// will be used (ex: "bevy_app"). Defaults to "true" + #[darling(default)] + pub meta: bool, +} + +pub struct Modules { + pub bevy_property: String, +} + +impl Modules { + pub fn meta() -> Modules { + Modules { + bevy_property: "bevy::property".to_string(), + } + } + + pub fn external() -> Modules { + Modules { + bevy_property: "bevy_property".to_string(), + } + } +} + +#[cfg(feature = "default_bevy_meta")] +impl Default for ModuleAttributeArgs { + fn default() -> Self { + ModuleAttributeArgs { + bevy_property: None, + meta: true, + } + } +} + +#[cfg(not(feature = "default_bevy_meta"))] +impl Default for ModuleAttributeArgs { + fn default() -> Self { + ModuleAttributeArgs { + bevy_property: None, + meta: false, + } + } +} + + +pub static MODULE_ATTRIBUTE_NAME: &'static str = "module"; + +pub fn get_modules(ast: &DeriveInput) -> Modules { + let module_attribute_args = ast + .attrs + .iter() + .find(|a| a.path.get_ident().as_ref().unwrap().to_string() == MODULE_ATTRIBUTE_NAME) + .map(|a| { + ModuleAttributeArgs::from_meta(&a.parse_meta().unwrap()) + .unwrap_or_else(|_err| ModuleAttributeArgs::default()) + }); + if let Some(module_attribute_args) = module_attribute_args { + let mut modules = if module_attribute_args.meta { + Modules::meta() + } else { + Modules::external() + }; + + if let Some(path) = module_attribute_args.bevy_property { + modules.bevy_property = path; + } + + modules + } else { + Modules::meta() + } +} + +pub fn get_path(path_str: &str) -> Path { + syn::parse(path_str.parse::().unwrap()).unwrap() +} diff --git a/crates/bevy_property/src/lib.rs b/crates/bevy_property/src/lib.rs index fa0164fbf3..57a66a4e50 100644 --- a/crates/bevy_property/src/lib.rs +++ b/crates/bevy_property/src/lib.rs @@ -8,4 +8,5 @@ pub use property::*; pub use properties::*; pub use dynamic_properties::*; +pub use bevy_property_derive::*; pub use serde; \ No newline at end of file