Skip to content

Commit 6e19af7

Browse files
committed
Support for joining Vec and json values
Signed-off-by: Michael X. Grey <[email protected]>
1 parent e94c170 commit 6e19af7

File tree

6 files changed

+323
-38
lines changed

6 files changed

+323
-38
lines changed

macros/src/buffer.rs

+17-10
Original file line numberDiff line numberDiff line change
@@ -208,19 +208,17 @@ fn impl_buffer_map_layout(
208208

209209
Ok(quote! {
210210
impl #impl_generics ::bevy_impulse::BufferMapLayout for #struct_ident #ty_generics #where_clause {
211-
fn buffer_list(&self) -> ::smallvec::SmallVec<[AnyBuffer; 8]> {
212-
use smallvec::smallvec;
213-
smallvec![#(
214-
::bevy_impulse::AsAnyBuffer::as_any_buffer(&self.#field_ident),
215-
)*]
216-
}
217-
218211
fn try_from_buffer_map(buffers: &::bevy_impulse::BufferMap) -> Result<Self, ::bevy_impulse::IncompatibleLayout> {
219212
let mut compatibility = ::bevy_impulse::IncompatibleLayout::default();
220213
#(
221-
let #field_ident = if let Ok(buffer) = compatibility.require_buffer_by_literal::<#buffer>(#map_key, buffers) {
222-
buffer
223-
} else {
214+
let #field_ident = compatibility.require_buffer_by_literal::<#buffer>(#map_key, buffers);
215+
)*
216+
217+
// Unwrap the Ok after inspecting every field so that the
218+
// IncompatibleLayout error can include all information about
219+
// which fields were incompatible.
220+
#(
221+
let Ok(#field_ident) = #field_ident else {
224222
return Err(compatibility);
225223
};
226224
)*
@@ -232,6 +230,15 @@ fn impl_buffer_map_layout(
232230
})
233231
}
234232
}
233+
234+
impl #impl_generics ::bevy_impulse::BufferMapStruct for #struct_ident #ty_generics #where_clause {
235+
fn buffer_list(&self) -> ::smallvec::SmallVec<[AnyBuffer; 8]> {
236+
use smallvec::smallvec;
237+
smallvec![#(
238+
::bevy_impulse::AsAnyBuffer::as_any_buffer(&self.#field_ident),
239+
)*]
240+
}
241+
}
235242
}
236243
.into())
237244
}

src/buffer/buffer_map.rs

+101-19
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ use smallvec::SmallVec;
2424
use bevy_ecs::prelude::{Entity, World};
2525

2626
use crate::{
27-
add_listener_to_source, Accessed, AddOperation, AnyBuffer, AnyBufferKey, AnyMessageBox, AsAnyBuffer,
28-
BufferKeyBuilder, Bufferable, Buffered, Builder, Chain, Gate, GateState, Join, Joined,
29-
OperationError, OperationResult, OperationRoster, Output, UnusedTarget,
27+
add_listener_to_source, Accessed, AddOperation, AnyBuffer, AnyBufferKey, AnyMessageBox,
28+
AsAnyBuffer, Buffer, BufferKeyBuilder, Bufferable, Buffered, Builder, Chain, Gate, GateState,
29+
Join, Joined, OperationError, OperationResult, OperationRoster, Output, UnusedTarget,
3030
};
3131

3232
pub use bevy_impulse_derive::JoinedValue;
@@ -92,15 +92,32 @@ impl AddBufferToMap for BufferMap {
9292
#[derive(ThisError, Debug, Clone, Default)]
9393
#[error("the incoming buffer map is incompatible with the layout")]
9494
pub struct IncompatibleLayout {
95-
/// Names of buffers that were missing from the incoming buffer map.
95+
/// Identities of buffers that were missing from the incoming buffer map.
9696
pub missing_buffers: Vec<BufferIdentifier<'static>>,
97+
/// Identities of buffers in the incoming buffer map which cannot exist in
98+
/// the target layout.
99+
pub forbidden_buffers: Vec<BufferIdentifier<'static>>,
97100
/// Buffers whose expected type did not match the received type.
98101
pub incompatible_buffers: Vec<BufferIncompatibility>,
99102
}
100103

101104
impl IncompatibleLayout {
102-
/// Check whether a named buffer is compatible with a specialized buffer type,
103-
/// such as `JsonBuffer`.
105+
/// Convert this into an error if it has any contents inside.
106+
pub fn as_result(self) -> Result<(), Self> {
107+
if !self.missing_buffers.is_empty() {
108+
return Err(self);
109+
}
110+
111+
if !self.incompatible_buffers.is_empty() {
112+
return Err(self);
113+
}
114+
115+
Ok(())
116+
}
117+
118+
/// Same as [`Self::require_buffer_by_literal`], but can be used with
119+
/// temporary borrows of a string slice. The string slice will be cloned if
120+
/// an error message needs to be produced.
104121
pub fn require_buffer_by_name<BufferType: 'static>(
105122
&mut self,
106123
expected_name: &str,
@@ -118,20 +135,38 @@ impl IncompatibleLayout {
118135
});
119136
}
120137
} else {
121-
self.missing_buffers.push(BufferIdentifier::Name(Cow::Owned(expected_name.to_owned())));
138+
self.missing_buffers
139+
.push(BufferIdentifier::Name(Cow::Owned(expected_name.to_owned())));
122140
}
123141

124142
Err(())
125143
}
126144

127-
/// Same as [`Self::require_buffer_by_name`] but more efficient for names
128-
/// given by string literal values.
145+
/// Check whether a named buffer is compatible with the required buffer type.
129146
pub fn require_buffer_by_literal<BufferType: 'static>(
130147
&mut self,
131148
expected_name: &'static str,
132149
buffers: &BufferMap,
133150
) -> Result<BufferType, ()> {
134-
let identifier = BufferIdentifier::Name(Cow::Borrowed(expected_name));
151+
self.require_buffer::<BufferType>(BufferIdentifier::literal_name(expected_name), buffers)
152+
}
153+
154+
/// Check whether an indexed buffer is compatible with the required buffer type.
155+
pub fn require_buffer_by_index<BufferType: 'static>(
156+
&mut self,
157+
expected_index: usize,
158+
buffers: &BufferMap,
159+
) -> Result<BufferType, ()> {
160+
self.require_buffer::<BufferType>(BufferIdentifier::Index(expected_index), buffers)
161+
}
162+
163+
/// Check whether the buffer associated with the identifier is compatible with
164+
/// the required buffer type.
165+
pub fn require_buffer<BufferType: 'static>(
166+
&mut self,
167+
identifier: BufferIdentifier<'static>,
168+
buffers: &BufferMap,
169+
) -> Result<BufferType, ()> {
135170
if let Some(buffer) = buffers.get(&identifier) {
136171
if let Some(buffer) = buffer.downcast_buffer::<BufferType>() {
137172
return Ok(buffer);
@@ -167,22 +202,26 @@ pub struct BufferIncompatibility {
167202
/// `#[derive(JoinedValue)]` on a struct that you want a join operation to
168203
/// produce.
169204
pub trait BufferMapLayout: Sized + Clone + 'static + Send + Sync {
170-
/// Produce a list of the buffers that exist in this layout.
171-
fn buffer_list(&self) -> SmallVec<[AnyBuffer; 8]>;
172-
173205
/// Try to convert a generic [`BufferMap`] into this specific layout.
174206
fn try_from_buffer_map(buffers: &BufferMap) -> Result<Self, IncompatibleLayout>;
175207
}
176208

177-
impl<T: BufferMapLayout> Bufferable for T {
209+
/// This trait helps auto-generated buffer map structs to implement the Buffered
210+
/// trait.
211+
pub trait BufferMapStruct: Sized + Clone + 'static + Send + Sync {
212+
/// Produce a list of the buffers that exist in this layout.
213+
fn buffer_list(&self) -> SmallVec<[AnyBuffer; 8]>;
214+
}
215+
216+
impl<T: BufferMapStruct> Bufferable for T {
178217
type BufferType = Self;
179218

180219
fn into_buffer(self, _: &mut Builder) -> Self::BufferType {
181220
self
182221
}
183222
}
184223

185-
impl<T: BufferMapLayout> Buffered for T {
224+
impl<T: BufferMapStruct> Buffered for T {
186225
fn verify_scope(&self, scope: Entity) {
187226
for buffer in self.buffer_list() {
188227
assert_eq!(buffer.scope(), scope);
@@ -285,14 +324,17 @@ pub trait BufferKeyMap: 'static + Send + Sync + Sized + Clone {
285324
}
286325

287326
impl BufferMapLayout for BufferMap {
288-
fn buffer_list(&self) -> SmallVec<[AnyBuffer; 8]> {
289-
self.values().cloned().collect()
290-
}
291327
fn try_from_buffer_map(buffers: &BufferMap) -> Result<Self, IncompatibleLayout> {
292328
Ok(buffers.clone())
293329
}
294330
}
295331

332+
impl BufferMapStruct for BufferMap {
333+
fn buffer_list(&self) -> SmallVec<[AnyBuffer; 8]> {
334+
self.values().cloned().collect()
335+
}
336+
}
337+
296338
impl Joined for BufferMap {
297339
type Item = HashMap<BufferIdentifier<'static>, AnyMessageBox>;
298340

@@ -351,9 +393,49 @@ impl Accessed for BufferMap {
351393
}
352394
}
353395

396+
impl<T: 'static + Send + Sync> JoinedValue for Vec<T> {
397+
type Buffers = Vec<Buffer<T>>;
398+
}
399+
400+
impl<B: 'static + Send + Sync + AsAnyBuffer + Clone> BufferMapLayout for Vec<B> {
401+
fn try_from_buffer_map(buffers: &BufferMap) -> Result<Self, IncompatibleLayout> {
402+
let mut downcast_buffers = Vec::new();
403+
let mut compatibility = IncompatibleLayout::default();
404+
for i in 0..buffers.len() {
405+
if let Ok(downcast) = compatibility.require_buffer_by_index::<B>(i, buffers) {
406+
downcast_buffers.push(downcast);
407+
}
408+
}
409+
410+
compatibility.as_result()?;
411+
Ok(downcast_buffers)
412+
}
413+
}
414+
415+
impl<T: 'static + Send + Sync, const N: usize> JoinedValue for SmallVec<[T; N]> {
416+
type Buffers = SmallVec<[Buffer<T>; N]>;
417+
}
418+
419+
impl<B: 'static + Send + Sync + AsAnyBuffer + Clone, const N: usize> BufferMapLayout
420+
for SmallVec<[B; N]>
421+
{
422+
fn try_from_buffer_map(buffers: &BufferMap) -> Result<Self, IncompatibleLayout> {
423+
let mut downcast_buffers = SmallVec::new();
424+
let mut compatibility = IncompatibleLayout::default();
425+
for i in 0..buffers.len() {
426+
if let Ok(downcast) = compatibility.require_buffer_by_index::<B>(i, buffers) {
427+
downcast_buffers.push(downcast);
428+
}
429+
}
430+
431+
compatibility.as_result()?;
432+
Ok(downcast_buffers)
433+
}
434+
}
435+
354436
#[cfg(test)]
355437
mod tests {
356-
use crate::{prelude::*, testing::*, BufferMap, AddBufferToMap};
438+
use crate::{prelude::*, testing::*, AddBufferToMap, BufferMap};
357439

358440
#[derive(JoinedValue)]
359441
struct TestJoinedValue<T: Send + Sync + 'static + Clone> {

src/buffer/bufferable.rs

+7
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,13 @@ impl<T: Bufferable, const N: usize> Bufferable for [T; N] {
152152
}
153153
}
154154

155+
impl<T: Bufferable> Bufferable for Vec<T> {
156+
type BufferType = Vec<T::BufferType>;
157+
fn into_buffer(self, builder: &mut Builder) -> Self::BufferType {
158+
self.into_iter().map(|b| b.into_buffer(builder)).collect()
159+
}
160+
}
161+
155162
pub trait IterBufferable {
156163
type BufferElement: Buffered + Joined;
157164

src/buffer/buffered.rs

+97
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,103 @@ impl<T: Accessed, const N: usize> Accessed for SmallVec<[T; N]> {
662662
}
663663
}
664664

665+
impl<B: Buffered> Buffered for Vec<B> {
666+
fn verify_scope(&self, scope: Entity) {
667+
for buffer in self {
668+
buffer.verify_scope(scope);
669+
}
670+
}
671+
672+
fn buffered_count(&self, session: Entity, world: &World) -> Result<usize, OperationError> {
673+
let mut min_count = None;
674+
for buffer in self {
675+
let count = buffer.buffered_count(session, world)?;
676+
if !min_count.is_some_and(|min| min < count) {
677+
min_count = Some(count);
678+
}
679+
}
680+
681+
Ok(min_count.unwrap_or(0))
682+
}
683+
684+
fn add_listener(&self, listener: Entity, world: &mut World) -> OperationResult {
685+
for buffer in self {
686+
buffer.add_listener(listener, world)?;
687+
}
688+
Ok(())
689+
}
690+
691+
fn gate_action(
692+
&self,
693+
session: Entity,
694+
action: Gate,
695+
world: &mut World,
696+
roster: &mut OperationRoster,
697+
) -> OperationResult {
698+
for buffer in self {
699+
buffer.gate_action(session, action, world, roster)?;
700+
}
701+
Ok(())
702+
}
703+
704+
fn as_input(&self) -> SmallVec<[Entity; 8]> {
705+
self.iter().flat_map(|buffer| buffer.as_input()).collect()
706+
}
707+
708+
fn ensure_active_session(&self, session: Entity, world: &mut World) -> OperationResult {
709+
for buffer in self {
710+
buffer.ensure_active_session(session, world)?;
711+
}
712+
713+
Ok(())
714+
}
715+
}
716+
717+
impl<B: Joined> Joined for Vec<B> {
718+
type Item = Vec<B::Item>;
719+
fn pull(&self, session: Entity, world: &mut World) -> Result<Self::Item, OperationError> {
720+
self.iter()
721+
.map(|buffer| buffer.pull(session, world))
722+
.collect()
723+
}
724+
}
725+
726+
impl<B: Accessed> Accessed for Vec<B> {
727+
type Key = Vec<B::Key>;
728+
fn add_accessor(&self, accessor: Entity, world: &mut World) -> OperationResult {
729+
for buffer in self {
730+
buffer.add_accessor(accessor, world)?;
731+
}
732+
Ok(())
733+
}
734+
735+
fn create_key(&self, builder: &BufferKeyBuilder) -> Self::Key {
736+
let mut keys = Vec::new();
737+
for buffer in self {
738+
keys.push(buffer.create_key(builder));
739+
}
740+
keys
741+
}
742+
743+
fn deep_clone_key(key: &Self::Key) -> Self::Key {
744+
let mut keys = Vec::new();
745+
for k in key {
746+
keys.push(B::deep_clone_key(k));
747+
}
748+
keys
749+
}
750+
751+
fn is_key_in_use(key: &Self::Key) -> bool {
752+
for k in key {
753+
if B::is_key_in_use(k) {
754+
return true;
755+
}
756+
}
757+
758+
false
759+
}
760+
}
761+
665762
pub(crate) fn add_listener_to_source(
666763
source: Entity,
667764
listener: Entity,

0 commit comments

Comments
 (0)