Skip to content

Commit 68681e4

Browse files
committed
derive BufferKeyMap
Signed-off-by: Teo Koon Peng <[email protected]>
1 parent 5dbfa63 commit 68681e4

File tree

5 files changed

+145
-52
lines changed

5 files changed

+145
-52
lines changed

macros/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ proc-macro = true
1616
[dependencies]
1717
syn = "2.0"
1818
quote = "1.0"
19+
proc-macro2 = "1.0.93"

macros/src/buffer.rs

Lines changed: 112 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,86 @@ use crate::Result;
66

77
pub(crate) fn impl_joined_value(ast: DeriveInput) -> Result<TokenStream> {
88
let struct_ident = ast.ident;
9-
let (field_ident, field_type) = match ast.data {
10-
syn::Data::Struct(data) => get_fields_map(data.fields)?,
9+
let (field_ident, field_type): (Vec<Ident>, Vec<Type>) = match ast.data {
10+
syn::Data::Struct(data) => get_fields_map(data.fields)?.into_iter().unzip(),
1111
_ => return Err("expected struct".to_string()),
1212
};
1313
let map_key: Vec<String> = field_ident.iter().map(|v| v.to_string()).collect();
1414
let struct_buffer_ident = format_ident!("__{}Buffers", struct_ident);
1515

16+
let impl_buffer_map_layout =
17+
buffer_map_layout(&struct_ident, &field_ident, &field_type, &map_key);
18+
1619
let gen = quote! {
17-
impl BufferMapLayout for #struct_ident {
20+
#impl_buffer_map_layout
21+
22+
impl ::bevy_impulse::JoinedValue for #struct_ident {
23+
type Buffers = #struct_buffer_ident;
24+
25+
fn pull(
26+
buffers: &::bevy_impulse::BufferMap,
27+
session: ::bevy_ecs::prelude::Entity,
28+
world: &mut ::bevy_ecs::prelude::World,
29+
) -> Result<Self, ::bevy_impulse::OperationError> {
30+
use ::bevy_impulse::{ManageBuffer, OrBroken};
31+
32+
#(
33+
let #field_ident = world
34+
.get_entity_mut(buffers.get(#map_key).or_broken()?.id())
35+
.or_broken()?
36+
.pull_from_buffer::<#field_type>(session)?;
37+
)*
38+
39+
Ok(Self {
40+
#(
41+
#field_ident
42+
),*
43+
})
44+
}
45+
}
46+
47+
struct #struct_buffer_ident {
48+
#(
49+
#field_ident: ::bevy_impulse::Buffer<#field_type>
50+
),*
51+
}
52+
53+
impl From<#struct_buffer_ident> for ::bevy_impulse::BufferMap {
54+
fn from(value: #struct_buffer_ident) -> Self {
55+
let mut buffers = ::bevy_impulse::BufferMap::default();
56+
#(
57+
buffers.insert(std::borrow::Cow::Borrowed(#map_key), value.#field_ident);
58+
)*
59+
buffers
60+
}
61+
}
62+
};
63+
64+
Ok(gen.into())
65+
}
66+
67+
fn get_fields_map(fields: syn::Fields) -> Result<Vec<(Ident, Type)>> {
68+
match fields {
69+
syn::Fields::Named(data) => {
70+
let mut idents_types = Vec::with_capacity(data.named.len());
71+
for field in data.named {
72+
let ident = field.ident.ok_or("expected named fields".to_string())?;
73+
idents_types.push((ident, field.ty));
74+
}
75+
Ok(idents_types)
76+
}
77+
_ => return Err("expected named fields".to_string()),
78+
}
79+
}
80+
81+
fn buffer_map_layout(
82+
struct_ident: &Ident,
83+
field_ident: &Vec<Ident>,
84+
field_type: &Vec<Type>,
85+
map_key: &Vec<String>,
86+
) -> proc_macro2::TokenStream {
87+
quote! {
88+
impl ::bevy_impulse::BufferMapLayout for #struct_ident {
1889
fn is_compatible(buffers: &BufferMap) -> Result<(), ::bevy_impulse::IncompatibleLayout> {
1990
let mut compatibility = ::bevy_impulse::IncompatibleLayout::default();
2091
#(
@@ -61,64 +132,57 @@ pub(crate) fn impl_joined_value(ast: DeriveInput) -> Result<TokenStream> {
61132
Ok(())
62133
}
63134
}
135+
}
136+
}
64137

65-
impl ::bevy_impulse::JoinedValue for #struct_ident {
66-
type Buffers = #struct_buffer_ident;
138+
pub(crate) fn impl_buffer_key_map(ast: DeriveInput) -> Result<TokenStream> {
139+
let struct_ident = ast.ident;
140+
let (field_ident, field_type): (Vec<Ident>, Vec<Type>) = match ast.data {
141+
syn::Data::Struct(data) => get_fields_map(data.fields)?.into_iter().unzip(),
142+
_ => return Err("expected struct".to_string()),
143+
};
144+
let map_key: Vec<String> = field_ident.iter().map(|v| v.to_string()).collect();
67145

68-
fn pull(
69-
buffers: &::bevy_impulse::BufferMap,
70-
session: ::bevy_ecs::prelude::Entity,
71-
world: &mut ::bevy_ecs::prelude::World,
72-
) -> Result<Self, ::bevy_impulse::OperationError> {
73-
use ::bevy_impulse::{ManageBuffer, OrBroken};
146+
let impl_buffer_map_layout =
147+
buffer_map_layout(&struct_ident, &field_ident, &field_type, &map_key);
148+
149+
// FIXME(koonpeng): `create_key` does not allow failure and we can't guarantee that the buffer
150+
// from buffer is valid.
151+
let gen = quote! {
152+
impl ::bevy_impulse::BufferKeyMap for #struct_ident {
153+
fn add_accessor(buffers: &::bevy_impulse::BufferMap, accessor: ::bevy_ecs::prelude::Entity, world: &mut ::bevy_ecs::prelude::World) -> ::bevy_impulse::OperationResult {
154+
use ::bevy_impulse::{Accessed, OrBroken};
74155

75156
#(
76-
let #field_ident = world
77-
.get_entity_mut(buffers.get(#map_key).or_broken()?.id())
78-
.or_broken()?
79-
.pull_from_buffer::<#field_type>(session)?;
157+
buffers.get(#map_key).or_broken()?.add_accessor(accessor, world)?;
80158
)*
81159

82-
Ok(Self {
83-
#(
84-
#field_ident
85-
),*
86-
})
160+
Ok(())
87161
}
88-
}
89162

90-
struct #struct_buffer_ident {
91-
#(
92-
#field_ident: ::bevy_impulse::Buffer<#field_type>
93-
),*
94-
}
163+
fn create_key(buffers: &::bevy_impulse::BufferMap, builder: &::bevy_impulse::BufferKeyBuilder) -> Self {
164+
use ::bevy_impulse::{Accessed, OrBroken};
95165

96-
impl From<#struct_buffer_ident> for ::bevy_impulse::BufferMap {
97-
fn from(value: #struct_buffer_ident) -> Self {
98-
let mut buffers = ::bevy_impulse::BufferMap::default();
166+
Self {#(
167+
#field_ident: buffers.get(#map_key).unwrap().create_key(builder).try_into().unwrap(),
168+
)*}
169+
}
170+
171+
fn deep_clone_key(&self) -> Self {
172+
Self {#(
173+
#field_ident: self.#field_ident.deep_clone(),
174+
)*}
175+
}
176+
177+
fn is_key_in_use(&self) -> bool {
99178
#(
100-
buffers.insert(std::borrow::Cow::Borrowed(#map_key), value.#field_ident);
101-
)*
102-
buffers
179+
self.#field_ident.is_in_use()
180+
)||*
103181
}
104182
}
183+
184+
#impl_buffer_map_layout
105185
};
106186

107187
Ok(gen.into())
108188
}
109-
110-
fn get_fields_map(fields: syn::Fields) -> Result<(Vec<Ident>, Vec<Type>)> {
111-
match fields {
112-
syn::Fields::Named(data) => {
113-
let mut idents = Vec::with_capacity(data.named.len());
114-
let mut types = Vec::with_capacity(data.named.len());
115-
for field in data.named {
116-
let ident = field.ident.ok_or("expected named fields".to_string())?;
117-
idents.push(ident);
118-
types.push(field.ty);
119-
}
120-
Ok((idents, types))
121-
}
122-
_ => return Err("expected named fields".to_string()),
123-
}
124-
}

macros/src/lib.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717

1818
mod buffer;
19-
use buffer::impl_joined_value;
19+
use buffer::{impl_buffer_key_map, impl_joined_value};
2020

2121
use proc_macro::TokenStream;
2222
use quote::quote;
@@ -76,3 +76,15 @@ pub fn derive_joined_value(input: TokenStream) -> TokenStream {
7676
.into(),
7777
}
7878
}
79+
80+
#[proc_macro_derive(BufferKeyMap)]
81+
pub fn derive_buffer_key_map(input: TokenStream) -> TokenStream {
82+
let input = parse_macro_input!(input as DeriveInput);
83+
match impl_buffer_key_map(input) {
84+
Ok(tokens) => tokens,
85+
Err(msg) => quote! {
86+
compile_error!(#msg);
87+
}
88+
.into(),
89+
}
90+
}

src/buffer/any_buffer.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,11 @@ impl AnyBufferKey {
176176
self.tag.session
177177
}
178178

179-
fn is_in_use(&self) -> bool {
179+
pub fn is_in_use(&self) -> bool {
180180
self.tag.is_in_use()
181181
}
182182

183-
fn deep_clone(&self) -> Self {
183+
pub fn deep_clone(&self) -> Self {
184184
Self {
185185
tag: self.tag.deep_clone(),
186186
interface: self.interface,
@@ -207,6 +207,14 @@ impl<T: 'static + Send + Sync + Any> From<BufferKey<T>> for AnyBufferKey {
207207
}
208208
}
209209

210+
impl<T: 'static + Send + Sync + Any> TryFrom<AnyBufferKey> for BufferKey<T> {
211+
type Error = OperationError;
212+
213+
fn try_from(value: AnyBufferKey) -> Result<Self, Self::Error> {
214+
value.downcast_for_message().or_broken()
215+
}
216+
}
217+
210218
/// Similar to [`BufferView`][crate::BufferView], but this can be unlocked with
211219
/// an [`AnyBufferKey`], so it can work for any buffer whose message types
212220
/// support serialization and deserialization.

src/buffer/buffer_map.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::{
2929
OperationRoster, Output, UnusedTarget,
3030
};
3131

32-
pub use bevy_impulse_derive::JoinedValue;
32+
pub use bevy_impulse_derive::{BufferKeyMap, JoinedValue};
3333

3434
#[derive(Clone, Default)]
3535
pub struct BufferMap {
@@ -409,4 +409,12 @@ mod tests {
409409
assert_eq!(value.string, "hello");
410410
assert!(context.no_unhandled_errors());
411411
}
412+
413+
// TODO(koonpeng): Add test that uses `TestBufferKeyMap`.
414+
#[derive(Clone, BufferKeyMap)]
415+
struct TestBufferKeyMap {
416+
integer: BufferKey<i64>,
417+
float: BufferKey<f64>,
418+
string: AnyBufferKey,
419+
}
412420
}

0 commit comments

Comments
 (0)