move derive(Properties) into bevy_properties
This commit is contained in:
parent
b7305046cf
commit
d3e0196cbb
@ -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))]
|
||||
pub fn derive_uniform(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
@ -11,8 +11,6 @@ pub struct ModuleAttributeArgs {
|
||||
#[darling(default)]
|
||||
pub bevy_core: Option<String>,
|
||||
#[darling(default)]
|
||||
pub bevy_property: Option<String>,
|
||||
#[darling(default)]
|
||||
pub bevy_app: Option<String>,
|
||||
#[darling(default)]
|
||||
pub legion: Option<String>,
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -8,4 +8,5 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
serde = "1"
|
||||
erased-serde = "0.3"
|
||||
erased-serde = "0.3"
|
||||
bevy_property_derive = { path = "bevy_property_derive" }
|
||||
17
crates/bevy_property/bevy_property_derive/Cargo.toml
Normal file
17
crates/bevy_property/bevy_property_derive/Cargo.toml
Normal 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"
|
||||
141
crates/bevy_property/bevy_property_derive/src/lib.rs
Normal file
141
crates/bevy_property/bevy_property_derive/src/lib.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
84
crates/bevy_property/bevy_property_derive/src/modules.rs
Normal file
84
crates/bevy_property/bevy_property_derive/src/modules.rs
Normal 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()
|
||||
}
|
||||
@ -8,4 +8,5 @@ pub use property::*;
|
||||
pub use properties::*;
|
||||
pub use dynamic_properties::*;
|
||||
|
||||
pub use bevy_property_derive::*;
|
||||
pub use serde;
|
||||
Loading…
Reference in New Issue
Block a user