move derive(Properties) into bevy_properties

This commit is contained in:
Carter Anderson 2020-05-24 11:01:48 -07:00
parent b7305046cf
commit d3e0196cbb
7 changed files with 245 additions and 142 deletions

View File

@ -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::<Vec<String>>();
let field_idents = fields.iter().map(|field| field.ident.as_ref().unwrap()).collect::<Vec<_>>();
let field_count = fields.len();
let field_indices = (0..field_count).collect::<Vec<usize>>();
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::<Self>()
}
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<dyn #bevy_property_path::Property> {
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))] #[proc_macro_derive(Uniform, attributes(uniform, module))]
pub fn derive_uniform(input: TokenStream) -> TokenStream { pub fn derive_uniform(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput); let ast = parse_macro_input!(input as DeriveInput);

View File

@ -11,8 +11,6 @@ pub struct ModuleAttributeArgs {
#[darling(default)] #[darling(default)]
pub bevy_core: Option<String>, pub bevy_core: Option<String>,
#[darling(default)] #[darling(default)]
pub bevy_property: Option<String>,
#[darling(default)]
pub bevy_app: Option<String>, pub bevy_app: Option<String>,
#[darling(default)] #[darling(default)]
pub legion: Option<String>, pub legion: Option<String>,
@ -27,7 +25,6 @@ pub struct Modules {
pub bevy_render: String, pub bevy_render: String,
pub bevy_asset: String, pub bevy_asset: String,
pub bevy_core: String, pub bevy_core: String,
pub bevy_property: String,
pub bevy_app: String, pub bevy_app: String,
pub legion: String, pub legion: String,
} }
@ -38,7 +35,6 @@ impl Modules {
bevy_asset: "bevy::asset".to_string(), bevy_asset: "bevy::asset".to_string(),
bevy_render: "bevy::render".to_string(), bevy_render: "bevy::render".to_string(),
bevy_core: "bevy::core".to_string(), bevy_core: "bevy::core".to_string(),
bevy_property: "bevy::property".to_string(),
bevy_app: "bevy::app".to_string(), bevy_app: "bevy::app".to_string(),
legion: "bevy".to_string(), legion: "bevy".to_string(),
} }
@ -49,7 +45,6 @@ impl Modules {
bevy_asset: "bevy_asset".to_string(), bevy_asset: "bevy_asset".to_string(),
bevy_render: "bevy_render".to_string(), bevy_render: "bevy_render".to_string(),
bevy_core: "bevy_core".to_string(), bevy_core: "bevy_core".to_string(),
bevy_property: "bevy_property".to_string(),
bevy_app: "bevy_app".to_string(), bevy_app: "bevy_app".to_string(),
legion: "legion".to_string(), legion: "legion".to_string(),
} }
@ -62,7 +57,6 @@ impl Default for ModuleAttributeArgs {
bevy_asset: None, bevy_asset: None,
bevy_render: None, bevy_render: None,
bevy_core: None, bevy_core: None,
bevy_property: None,
bevy_app: None, bevy_app: None,
legion: None, legion: None,
meta: true, meta: true,
@ -96,10 +90,6 @@ pub fn get_modules(ast: &DeriveInput) -> Modules {
modules.bevy_render = path; 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 { if let Some(path) = module_attribute_args.bevy_core {
modules.bevy_core = path; modules.bevy_core = path;
} }

View File

@ -8,4 +8,5 @@ edition = "2018"
[dependencies] [dependencies]
serde = "1" serde = "1"
erased-serde = "0.3" erased-serde = "0.3"
bevy_property_derive = { path = "bevy_property_derive" }

View File

@ -0,0 +1,17 @@
[package]
name = "bevy_property_derive"
version = "0.1.0"
authors = ["Carter Anderson <mcanders1@gmail.com>"]
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"

View File

@ -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::<Vec<String>>();
let field_idents = fields
.iter()
.map(|field| field.ident.as_ref().unwrap())
.collect::<Vec<_>>();
let field_count = fields.len();
let field_indices = (0..field_count).collect::<Vec<usize>>();
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::<Self>()
}
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<dyn #bevy_property_path::Property> {
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)
}
}
})
}

View File

@ -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<String>,
/// 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::<TokenStream>().unwrap()).unwrap()
}

View File

@ -8,4 +8,5 @@ pub use property::*;
pub use properties::*; pub use properties::*;
pub use dynamic_properties::*; pub use dynamic_properties::*;
pub use bevy_property_derive::*;
pub use serde; pub use serde;