props: derive, get/set, example

This commit is contained in:
Carter Anderson 2020-05-21 23:58:11 -07:00
parent 9368242013
commit da8daa051b
6 changed files with 325 additions and 66 deletions

View file

@ -153,6 +153,10 @@ path = "examples/input/input_keyboard.rs"
name = "load_scene" name = "load_scene"
path = "examples/scene/load_scene.rs" path = "examples/scene/load_scene.rs"
[[example]]
name = "props"
path = "examples/scene/props.rs"
[[example]] [[example]]
name = "shader_custom_material" name = "shader_custom_material"
path = "examples/shader/shader_custom_material.rs" path = "examples/shader/shader_custom_material.rs"

View file

@ -50,6 +50,85 @@ 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_props_path = get_path(&modules.bevy_props);
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_props_path::Props for #struct_name#ty_generics {
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
fn prop(&self, name: &str) -> Option<&dyn #bevy_props_path::Prop> {
match name {
#(#field_names => Some(&self.#field_idents),)*
_ => None,
}
}
fn prop_mut(&mut self, name: &str) -> Option<&mut dyn #bevy_props_path::Prop> {
match name {
#(#field_names => Some(&mut self.#field_idents),)*
_ => None,
}
}
fn prop_with_index(&self, index: usize) -> Option<&dyn #bevy_props_path::Prop> {
match index {
#(#field_indices => Some(&self.#field_idents),)*
_ => None,
}
}
fn prop_with_index_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_props_path::Prop> {
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_props_path::PropIter {
#bevy_props_path::PropIter::new(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,6 +11,8 @@ pub struct ModuleAttributeArgs {
#[darling(default)] #[darling(default)]
pub bevy_core: Option<String>, pub bevy_core: Option<String>,
#[darling(default)] #[darling(default)]
pub bevy_props: 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>,
@ -25,6 +27,7 @@ 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_props: String,
pub bevy_app: String, pub bevy_app: String,
pub legion: String, pub legion: String,
} }
@ -35,6 +38,7 @@ 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_props: "bevy::props".to_string(),
bevy_app: "bevy::app".to_string(), bevy_app: "bevy::app".to_string(),
legion: "bevy".to_string(), legion: "bevy".to_string(),
} }
@ -45,6 +49,7 @@ 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_props: "bevy_props".to_string(),
bevy_app: "bevy_app".to_string(), bevy_app: "bevy_app".to_string(),
legion: "legion".to_string(), legion: "legion".to_string(),
} }
@ -57,6 +62,7 @@ impl Default for ModuleAttributeArgs {
bevy_asset: None, bevy_asset: None,
bevy_render: None, bevy_render: None,
bevy_core: None, bevy_core: None,
bevy_props: None,
bevy_app: None, bevy_app: None,
legion: None, legion: None,
meta: true, meta: true,

View file

@ -1,86 +1,170 @@
use serde::{ser::SerializeMap, Deserialize, Serialize}; use serde::{ser::SerializeMap, Deserialize, Serialize};
use std::{any::Any, collections::HashMap}; use std::{any::Any, collections::HashMap, borrow::Cow};
pub struct Test { pub struct DynamicScene {
a: usize, pub entities: Vec<SceneEntity>,
b: String,
} }
impl Props for Test { #[derive(Serialize, Deserialize)]
fn prop(&self, name: &str) -> Option<&dyn Prop> { pub struct SceneEntity {
match name { pub entity: u32,
"a" => Some(&self.a), pub components: Vec<DynamicProperties>,
"b" => Some(&self.b),
_ => None,
}
}
fn prop_mut(&mut self, name: &str) -> Option<&mut dyn Prop> {
match name {
"a" => Some(&mut self.a),
"b" => Some(&mut self.b),
_ => None,
}
}
fn prop_names(&self) -> Vec<&str> {
static NAMES: &[&str] = &["a", "b"];
NAMES.to_vec()
}
} }
#[derive(Default)] #[derive(Default)]
pub struct DynamicProps { pub struct DynamicProperties {
pub props: HashMap<String, Box<dyn Prop>>, pub type_name: &'static str,
pub props: Vec<(Cow<'static, str>, Box<dyn Prop>)>,
pub prop_indices: HashMap<Cow<'static, str>, usize>,
} }
impl DynamicProps { pub struct SerializableProps<'a> {
pub props: &'a dyn Props,
}
impl DynamicProperties {
fn push(&mut self, name: &str, prop: Box<dyn Prop>) {
let name: Cow<'static, str> = Cow::Owned(name.to_string());
self.props.push((name.clone(), prop));
self.prop_indices.insert(name, self.props.len());
}
pub fn set<T: Prop>(&mut self, name: &str, prop: T) { pub fn set<T: Prop>(&mut self, name: &str, prop: T) {
self.props.insert(name.to_string(), Box::new(prop)); if let Some(index) = self.prop_indices.get(name) {
self.props[*index].1 = Box::new(prop);
} else {
self.push(name, Box::new(prop));
}
}
pub fn set_box(&mut self, name: &str, prop: Box<dyn Prop>) {
if let Some(index) = self.prop_indices.get(name) {
self.props[*index].1 = prop;
} else {
self.push(name, prop);
}
} }
} }
impl Props for DynamicProps { impl Props for DynamicProperties {
fn type_name(&self) -> &str {
self.type_name
}
fn prop(&self, name: &str) -> Option<&dyn Prop> { fn prop(&self, name: &str) -> Option<&dyn Prop> {
self.props.get(name).map(|p| &**p) if let Some(index) = self.prop_indices.get(name) {
Some(&*self.props[*index].1)
} else {
None
}
} }
fn prop_mut(&mut self, name: &str) -> Option<&mut dyn Prop> { fn prop_mut(&mut self, name: &str) -> Option<&mut dyn Prop> {
self.props.get_mut(name).map(|p| &mut **p) if let Some(index) = self.prop_indices.get(name) {
Some(&mut *self.props[*index].1)
} else {
None
}
} }
fn prop_names(&self) -> Vec<&str> {
self.props.keys().map(|k| k.as_str()).collect::<Vec<&str>>() fn prop_with_index(&self, index: usize) -> Option<&dyn Prop> {
self.props.get(index).map(|(_i, prop)| &**prop)
}
fn prop_with_index_mut(&mut self, index: usize) -> Option<&mut dyn Prop> {
self.props.get_mut(index).map(|(_i, prop)| &mut **prop)
}
fn prop_name(&self, index: usize) -> Option<&str> {
self.props.get(index).map(|(name, _)| name.as_ref())
}
fn prop_len(&self) -> usize {
self.props.len()
}
fn iter_props(&self) -> PropIter {
PropIter {
props: self,
index: 0,
}
} }
} }
impl Serialize for DynamicProps { impl Serialize for DynamicProperties {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
S: serde::Serializer, S: serde::Serializer,
{ {
let mut state = serializer.serialize_map(Some(self.props.len()))?; let mut state = serializer.serialize_map(Some(self.props.len()))?;
for prop_name in self.prop_names() { state.serialize_entry("type", self.type_name())?;
let prop = self.prop(prop_name).unwrap(); for (name, prop) in self.iter_props() {
state.serialize_entry(prop_name, prop)?; state.serialize_entry(name, prop)?;
}
state.end()
}
}
impl<'a> Serialize for SerializableProps<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut state = serializer.serialize_map(Some(self.props.prop_len()))?;
state.serialize_entry("type", self.props.type_name())?;
for (name, prop) in self.props.iter_props() {
state.serialize_entry(name, prop)?;
} }
state.end() state.end()
// let mut state = serializer.serialize_struct("dyn", self.props.len())?;
// {
// for prop_name in self.prop_names() {
// let prop = self.prop(prop_name).unwrap();
// state.serialize_field(strrr, prop)?;
// }
// }
// state.end()
} }
} }
pub trait Props { pub trait Props {
fn type_name(&self) -> &str;
fn prop(&self, name: &str) -> Option<&dyn Prop>; fn prop(&self, name: &str) -> Option<&dyn Prop>;
fn prop_mut(&mut self, name: &str) -> Option<&mut dyn Prop>; fn prop_mut(&mut self, name: &str) -> Option<&mut dyn Prop>;
fn prop_names(&self) -> Vec<&str>; fn prop_with_index(&self, index: usize) -> Option<&dyn Prop>;
fn prop_with_index_mut(&mut self, index: usize) -> Option<&mut dyn Prop>;
fn prop_name(&self, index: usize) -> Option<&str>;
fn prop_len(&self) -> usize;
fn iter_props(&self) -> PropIter;
fn apply(&mut self, props: &dyn Props) { fn apply(&mut self, props: &dyn Props) {
for prop_name in props.prop_names() { for (name, prop) in props.iter_props() {
self.prop_mut(prop_name) self.prop_mut(name).unwrap().set_val_dyn(prop);
.unwrap() }
.set_prop_val(props.prop(prop_name).unwrap().clone()); }
fn to_dynamic(&self) -> DynamicProperties where Self: 'static {
let mut dynamic_props = DynamicProperties::default();
for (name, prop) in self.iter_props() {
dynamic_props.set_box(name, prop.clone_prop());
}
dynamic_props.type_name = std::any::type_name::<Self>();
dynamic_props
}
}
pub struct PropIter<'a> {
props: &'a dyn Props,
index: usize,
}
impl<'a> PropIter<'a> {
pub fn new(props: &'a dyn Props) -> Self {
PropIter {
props,
index: 0,
}
}
}
impl<'a> Iterator for PropIter<'a> {
type Item = (&'a str, &'a dyn Prop);
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.props.prop_len() {
let prop = self.props.prop_with_index(self.index).unwrap();
let name = self.props.prop_name(self.index).unwrap();
self.index += 1;
Some((name, prop))
} else {
None
} }
} }
} }
@ -88,42 +172,63 @@ pub trait Props {
pub trait Prop: erased_serde::Serialize + Send + Sync + Any + 'static { pub trait Prop: erased_serde::Serialize + Send + Sync + Any + 'static {
fn any(&self) -> &dyn Any; fn any(&self) -> &dyn Any;
fn any_mut(&mut self) -> &mut dyn Any; fn any_mut(&mut self) -> &mut dyn Any;
fn clone(&self) -> Box<dyn Any>; fn clone_prop(&self) -> Box<dyn Prop>;
fn type_name(&self) -> &str { fn set_val_dyn(&mut self, value: &dyn Prop);
std::any::type_name::<Self>()
}
} }
erased_serde::serialize_trait_object!(Prop); erased_serde::serialize_trait_object!(Prop);
pub trait PropVal { pub trait PropVal {
fn prop_val<T: 'static>(&self) -> Option<&T>; fn val<T: 'static>(&self) -> Option<&T>;
fn set_prop_val<T: 'static>(&mut self, value: T); fn set_val<T: 'static>(&mut self, value: T);
fn set_prop_val_boxed<T: 'static>(&mut self, value: Box<dyn Any>);
} }
pub trait PropsVal {
fn prop_val<T: 'static>(&self, name: &str) -> Option<&T>;
fn set_prop_val<T: 'static>(&mut self, name: &str, value: T);
fn set_prop_val_dyn<T: 'static>(&mut self, name: &str, value: &dyn Prop);
}
impl PropVal for dyn Prop { impl PropVal for dyn Prop {
fn prop_val<T: 'static>(&self) -> Option<&T> { fn val<T: 'static>(&self) -> Option<&T> {
self.any().downcast_ref::<T>() self.any().downcast_ref::<T>()
} }
fn set_prop_val<T: 'static>(&mut self, value: T) { fn set_val<T: 'static>(&mut self, value: T) {
if let Some(prop) = self.any_mut().downcast_mut::<T>() { if let Some(prop) = self.any_mut().downcast_mut::<T>() {
*prop = value; *prop = value;
} } else {
} panic!("prop value is not {}", std::any::type_name::<T>());
fn set_prop_val_boxed<T: 'static>(&mut self, value: Box<dyn Any>) {
if let Some(prop) = self.any_mut().downcast_mut::<T>() {
*prop = *value.downcast::<T>().unwrap();
} }
} }
} }
impl<'a> Deserialize<'a> for DynamicProps { impl<P> PropsVal for P where P: Props {
fn prop_val<T: 'static>(&self, name: &str) -> Option<&T> {
self.prop(name).and_then(|p| p.any().downcast_ref::<T>())
}
fn set_prop_val<T: 'static>(&mut self, name: &str, value: T) {
if let Some(prop) = self.prop_mut(name).and_then(|p| p.any_mut().downcast_mut::<T>()) {
*prop = value;
} else {
panic!("prop does not exist or is incorrect type: {}", name);
}
}
fn set_prop_val_dyn<T: 'static>(&mut self, name: &str, value: &dyn Prop) {
if let Some(prop) = self.prop_mut(name) {
prop.set_val_dyn(value);
} else {
panic!("prop does not exist: {}", name);
}
}
}
impl<'a> Deserialize<'a> for DynamicProperties {
fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
where where
D: serde::Deserializer<'a>, D: serde::Deserializer<'a>,
{ {
Ok(DynamicProps::default()) Ok(DynamicProperties::default())
} }
} }
@ -137,7 +242,14 @@ where
fn any_mut(&mut self) -> &mut dyn Any { fn any_mut(&mut self) -> &mut dyn Any {
self self
} }
fn clone(&self) -> Box<dyn Any> { fn clone_prop(&self) -> Box<dyn Prop> {
Box::new(self.clone()) Box::new(self.clone())
} }
fn set_val_dyn(&mut self, value: &dyn Prop) {
if let Some(prop) = value.any().downcast_ref::<T>() {
*self = prop.clone();
} else {
panic!("prop value is not {}", std::any::type_name::<T>());
}
}
} }

56
examples/scene/props.rs Normal file
View file

@ -0,0 +1,56 @@
use bevy::prelude::*;
use bevy_props::{DynamicScene, SerializableProps, SceneEntity};
use serde::ser::Serialize;
fn main() {
App::build()
.add_default_plugins()
.add_startup_system(setup.system())
.run();
}
#[derive(Properties)]
pub struct Test {
a: usize,
b: String,
}
fn setup() {
let mut test = Test {
a: 1,
b: "hi".to_string(),
};
test.set_prop_val::<usize>("a", 2);
assert_eq!(test.a, 2);
let mut patch = DynamicProperties::default();
patch.set::<usize>("a", 3);
test.apply(&patch);
assert_eq!(test.a, 3);
let ser = SerializableProps {
props: &test,
};
let mut serializer = ron::ser::Serializer::new(Some(ron::ser::PrettyConfig::default()), false);
ser.serialize(&mut serializer).unwrap();
println!("{}", serializer.into_output_string());
let dynamic_scene = DynamicScene {
entities: vec![
SceneEntity {
entity: 12345,
components: vec![
patch
]
}
]
};
let mut serializer = ron::ser::Serializer::new(Some(ron::ser::PrettyConfig::default()), false);
dynamic_scene.entities.serialize(&mut serializer).unwrap();
println!("{}", serializer.into_output_string());
}

View file

@ -11,6 +11,8 @@ pub use crate::derive::*;
pub use crate::diagnostic::DiagnosticsPlugin; pub use crate::diagnostic::DiagnosticsPlugin;
#[cfg(feature = "pbr")] #[cfg(feature = "pbr")]
pub use crate::pbr::{entity::*, light::Light, material::StandardMaterial}; pub use crate::pbr::{entity::*, light::Light, material::StandardMaterial};
#[cfg(feature = "props")]
pub use crate::props::{Prop, Props, PropVal, PropsVal, DynamicProperties};
#[cfg(feature = "render")] #[cfg(feature = "render")]
pub use crate::render::{ pub use crate::render::{
draw_target, draw_target,