Skip to content

Commit

Permalink
feat: Add derive macro for PrefabData
Browse files Browse the repository at this point in the history
  • Loading branch information
Rhuagh committed Oct 28, 2018
1 parent a5e1ce8 commit 64fc5e3
Show file tree
Hide file tree
Showing 11 changed files with 270 additions and 190 deletions.
1 change: 1 addition & 0 deletions amethyst_animation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ travis-ci = { repository = "amethyst/amethyst" }
[dependencies]
amethyst_assets = { path = "../amethyst_assets/", version = "0.5.0" }
amethyst_core = { path = "../amethyst_core/", version = "0.4.0" }
amethyst_derive = { path = "../amethyst_derive", version = "0.1.0" }
amethyst_renderer = { path = "../amethyst_renderer/", version = "0.9.0" }
derivative = "1.0"
fnv = "1"
Expand Down
2 changes: 2 additions & 0 deletions amethyst_animation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@

extern crate amethyst_assets;
extern crate amethyst_core;
#[macro_use]
extern crate amethyst_derive;
extern crate amethyst_renderer;
#[macro_use]
extern crate derivative;
Expand Down
1 change: 1 addition & 0 deletions amethyst_animation/src/prefab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ where
pub rest_state: Option<RestState<T>>,
}

// TODO: derive PrefabData for AnimatablePrefab, require generics support in derive macro
impl<'a, I, T> PrefabData<'a> for AnimatablePrefab<I, T>
where
T: AnimationSampling + Clone,
Expand Down
31 changes: 2 additions & 29 deletions amethyst_animation/src/skinning/resources.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use amethyst_assets::{PrefabData, PrefabError};
use amethyst_assets::{PrefabData, PrefabError, ProgressCounter};
use amethyst_core::cgmath::{Matrix4, SquareMatrix};
use amethyst_core::specs::prelude::{Component, DenseVecStorage, Entity, WriteStorage};
use amethyst_renderer::JointTransformsPrefab;
Expand Down Expand Up @@ -121,7 +121,7 @@ impl<'a> PrefabData<'a> for SkinPrefab {
}

/// `PrefabData` for full skinning support
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
#[derive(Clone, Default, Debug, Serialize, Deserialize, PrefabData)]
#[serde(default)]
pub struct SkinnablePrefab {
/// Place `Skin` on the `Entity`
Expand All @@ -131,30 +131,3 @@ pub struct SkinnablePrefab {
/// Place `JointTransforms` on the `Entity`
pub joint_transforms: Option<JointTransformsPrefab>,
}

impl<'a> PrefabData<'a> for SkinnablePrefab {
type SystemData = (
<SkinPrefab as PrefabData<'a>>::SystemData,
<JointPrefab as PrefabData<'a>>::SystemData,
<JointTransformsPrefab as PrefabData<'a>>::SystemData,
);
type Result = ();

fn add_to_entity(
&self,
entity: Entity,
system_data: &mut Self::SystemData,
entities: &[Entity],
) -> Result<(), PrefabError> {
if let Some(ref prefab) = self.skin {
prefab.add_to_entity(entity, &mut system_data.0, entities)?;
}
if let Some(ref prefab) = self.joint {
prefab.add_to_entity(entity, &mut system_data.1, entities)?;
}
if let Some(ref prefab) = self.joint_transforms {
prefab.add_to_entity(entity, &mut system_data.2, entities)?;
}
Ok(())
}
}
4 changes: 3 additions & 1 deletion amethyst_derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ travis-ci = { repository = "amethyst/amethyst" }
syn = { version = "0.15", features = ["visit"] }
quote = "0.6"
proc-macro2 = "0.4"
amethyst_core = { path = "../amethyst_core", version = "0.4.0" }

[dev-dependencies]
amethyst_core = { path = "../amethyst_core", version = "0.3.0" }

[lib]
name = "amethyst_derive"
Expand Down
112 changes: 112 additions & 0 deletions amethyst_derive/src/event_reader.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use proc_macro2::TokenStream;
use syn::{Data, DeriveInput, Ident, Meta, NestedMeta, Type};

pub fn impl_event_reader(ast: &DeriveInput) -> TokenStream {
let event_name = &ast.ident;

let mut reader_name: Option<Ident> = None;
for meta in ast
.attrs
.iter()
.filter(|attr| attr.path.segments[0].ident == "reader")
.map(|attr| {
attr.interpret_meta()
.expect("reader attribute incorrectly defined")
}) {
match meta {
Meta::List(l) => {
for nested_meta in l.nested.iter() {
match *nested_meta {
NestedMeta::Meta(Meta::Word(ref word)) => {
reader_name = Some(word.clone());
}
_ => panic!("reader attribute does not contain a single name"),
}
}
}
_ => (),
};
}

let reader_name = reader_name.expect(&format!(
r#"
#[derive(EventReader)] requested for {}, but #[reader(SomeEventReader)] attribute is missing
Example usage:
#[derive(EventReader)]
#[reader(SomeEventReader)]
pub enum SomeEvent {{
One(Event1),
Two(Event2),
}}
"#,
event_name
));

let tys = collect_field_types(&ast.data);
let tys = &tys;
let names = collect_variant_names(&ast.data);
let names = &names;

let reads : Vec<_> = (0..tys.len()).map(|n| {
let variant = &names[n];
quote! {
events.extend(data.#n.read(self.#n.as_mut().expect("ReaderId undefined, has setup been run?")).cloned().map(|e| #event_name::#variant(e)));
}
}).collect();
let setups: Vec<_> = (0..tys.len())
.map(|n| {
let ty = &tys[n];
quote! {
self.#n = Some(res.fetch_mut::<EventChannel<#ty>>().register_reader());
}
}).collect();
quote! {
#[allow(missing_docs)]
#[derive(Default)]
pub struct #reader_name(
#(Option<ReaderId<#tys>>, )*
);

impl<'a> EventReader<'a> for #reader_name {
type SystemData = (
#(Read<'a, EventChannel<#tys>>),*
);
type Event = #event_name;

fn read(&mut self, data: Self::SystemData, events: &mut Vec<#event_name>) {
#(#reads)*
}

fn setup(&mut self, res: &mut Resources) {
<Self::SystemData as SystemData<'a>>::setup(res);
#(#setups)*
}
}
}
}

fn collect_field_types(ast: &Data) -> Vec<Type> {
let variants = match *ast {
Data::Enum(ref variants) => &variants.variants,
_ => panic!("EventReader derive only support enums"),
};
variants
.iter()
.map(|v| {
v.fields
.iter()
.next()
.expect("Event enum variant does not contain an inner event type")
.ty
.clone()
}).collect()
}

fn collect_variant_names(ast: &Data) -> Vec<Ident> {
let variants = match *ast {
Data::Enum(ref variants) => &variants.variants,
_ => panic!("EventReader derive only support enums"),
};
variants.iter().map(|v| v.ident.clone()).collect()
}
121 changes: 10 additions & 111 deletions amethyst_derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#![recursion_limit = "256"]

extern crate amethyst_core;
extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
Expand All @@ -9,121 +8,21 @@ extern crate syn;
extern crate quote;

use proc_macro::TokenStream;
use syn::{Data, DeriveInput, Ident, Meta, NestedMeta, Type};
use syn::DeriveInput;

mod event_reader;
mod prefab_data;

#[proc_macro_derive(EventReader, attributes(reader))]
pub fn event_reader_derive(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let gen = impl_event_reader(&ast);
let gen = event_reader::impl_event_reader(&ast);
gen.into()
}

fn impl_event_reader(ast: &DeriveInput) -> proc_macro2::TokenStream {
let event_name = &ast.ident;

let mut reader_name: Option<Ident> = None;
for meta in ast
.attrs
.iter()
.filter(|attr| attr.path.segments[0].ident == "reader")
.map(|attr| {
attr.interpret_meta()
.expect("reader attribute incorrectly defined")
}) {
match meta {
Meta::List(l) => {
for nested_meta in l.nested.iter() {
match *nested_meta {
NestedMeta::Meta(Meta::Word(ref word)) => {
reader_name = Some(word.clone());
}
_ => panic!("reader attribute does not contain a single name"),
}
}
}
_ => (),
};
}

let reader_name = reader_name.expect(&format!(
r#"
#[derive(EventReader)] requested for {}, but #[reader(SomeEventReader)] attribute is missing
Example usage:
#[derive(EventReader)]
#[reader(SomeEventReader)]
pub enum SomeEvent {{
One(Event1),
Two(Event2),
}}
"#,
event_name
));

let tys = collect_field_types(&ast.data);
let tys = &tys;
let names = collect_variant_names(&ast.data);
let names = &names;

let reads : Vec<_> = (0..tys.len()).map(|n| {
let variant = &names[n];
quote! {
events.extend(data.#n.read(self.#n.as_mut().expect("ReaderId undefined, has setup been run?")).cloned().map(|e| #event_name::#variant(e)));
}
}).collect();
let setups: Vec<_> = (0..tys.len())
.map(|n| {
let ty = &tys[n];
quote! {
self.#n = Some(res.fetch_mut::<EventChannel<#ty>>().register_reader());
}
}).collect();
quote! {
#[allow(missing_docs)]
#[derive(Default)]
pub struct #reader_name(
#(Option<ReaderId<#tys>>, )*
);

impl<'a> EventReader<'a> for #reader_name {
type SystemData = (
#(Read<'a, EventChannel<#tys>>),*
);
type Event = #event_name;

fn read(&mut self, data: Self::SystemData, events: &mut Vec<#event_name>) {
#(#reads)*
}

fn setup(&mut self, res: &mut Resources) {
<Self::SystemData as SystemData<'a>>::setup(res);
#(#setups)*
}
}
}
}

fn collect_field_types(ast: &Data) -> Vec<Type> {
let variants = match *ast {
Data::Enum(ref variants) => &variants.variants,
_ => panic!("EventReader derive only support enums"),
};
variants
.iter()
.map(|v| {
v.fields
.iter()
.next()
.expect("Event enum variant does not contain an inner event type")
.ty
.clone()
}).collect()
}

fn collect_variant_names(ast: &Data) -> Vec<Ident> {
let variants = match *ast {
Data::Enum(ref variants) => &variants.variants,
_ => panic!("EventReader derive only support enums"),
};
variants.iter().map(|v| v.ident.clone()).collect()
#[proc_macro_derive(PrefabData, attributes(prefab))]
pub fn prefab_data_derive(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let gen = prefab_data::impl_prefab_data(&ast);
gen.into()
}
Loading

0 comments on commit 64fc5e3

Please sign in to comment.