Skip to content

Commit e94c170

Browse files
committed
Generalize buffer map keys
Signed-off-by: Michael X. Grey <[email protected]>
1 parent f64871e commit e94c170

File tree

7 files changed

+123
-75
lines changed

7 files changed

+123
-75
lines changed

macros/src/buffer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,14 +211,14 @@ fn impl_buffer_map_layout(
211211
fn buffer_list(&self) -> ::smallvec::SmallVec<[AnyBuffer; 8]> {
212212
use smallvec::smallvec;
213213
smallvec![#(
214-
self.#field_ident.as_any_buffer(),
214+
::bevy_impulse::AsAnyBuffer::as_any_buffer(&self.#field_ident),
215215
)*]
216216
}
217217

218218
fn try_from_buffer_map(buffers: &::bevy_impulse::BufferMap) -> Result<Self, ::bevy_impulse::IncompatibleLayout> {
219219
let mut compatibility = ::bevy_impulse::IncompatibleLayout::default();
220220
#(
221-
let #field_ident = if let Ok(buffer) = compatibility.require_buffer_type::<#buffer>(#map_key, buffers) {
221+
let #field_ident = if let Ok(buffer) = compatibility.require_buffer_by_literal::<#buffer>(#map_key, buffers) {
222222
buffer
223223
} else {
224224
return Err(compatibility);

src/buffer.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,6 @@ impl<T> Buffer<T> {
113113
pub fn location(&self) -> BufferLocation {
114114
self.location
115115
}
116-
117-
/// Cast this into an [`AnyBuffer`].
118-
pub fn as_any_buffer(&self) -> AnyBuffer
119-
where
120-
T: 'static + Send + Sync,
121-
{
122-
self.clone().into()
123-
}
124116
}
125117

126118
impl<T> Clone for Buffer<T> {

src/buffer/any_buffer.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,6 @@ impl AnyBuffer {
121121
.ok()
122122
.map(|x| *x)
123123
}
124-
125-
pub fn as_any_buffer(&self) -> Self {
126-
self.clone().into()
127-
}
128124
}
129125

130126
impl<T: 'static + Send + Sync + Any> From<Buffer<T>> for AnyBuffer {
@@ -137,6 +133,25 @@ impl<T: 'static + Send + Sync + Any> From<Buffer<T>> for AnyBuffer {
137133
}
138134
}
139135

136+
/// A trait for turning a buffer into an [`AnyBuffer`]. It is expected that all
137+
/// buffer types implement this trait.
138+
pub trait AsAnyBuffer {
139+
/// Convert this buffer into an [`AnyBuffer`].
140+
fn as_any_buffer(&self) -> AnyBuffer;
141+
}
142+
143+
impl AsAnyBuffer for AnyBuffer {
144+
fn as_any_buffer(&self) -> AnyBuffer {
145+
*self
146+
}
147+
}
148+
149+
impl<T: 'static + Send + Sync> AsAnyBuffer for Buffer<T> {
150+
fn as_any_buffer(&self) -> AnyBuffer {
151+
(*self).into()
152+
}
153+
}
154+
140155
/// Similar to a [`BufferKey`] except it can be used for any buffer without
141156
/// knowing the buffer's message type at compile time.
142157
///

src/buffer/buffer_map.rs

Lines changed: 83 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -24,27 +24,66 @@ use smallvec::SmallVec;
2424
use bevy_ecs::prelude::{Entity, World};
2525

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

3232
pub use bevy_impulse_derive::JoinedValue;
3333

34-
#[derive(Clone, Default)]
35-
pub struct BufferMap {
36-
inner: HashMap<Cow<'static, str>, AnyBuffer>,
34+
/// Uniquely identify a buffer within a buffer map, either by name or by an
35+
/// index value.
36+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
37+
pub enum BufferIdentifier<'a> {
38+
/// Identify a buffer by name
39+
Name(Cow<'a, str>),
40+
/// Identify a buffer by an index value
41+
Index(usize),
3742
}
3843

39-
impl BufferMap {
40-
/// Insert a named buffer into the map.
41-
pub fn insert(&mut self, name: impl Into<Cow<'static, str>>, buffer: impl Into<AnyBuffer>) {
42-
self.inner.insert(name.into(), buffer.into());
44+
impl BufferIdentifier<'static> {
45+
/// Clone a name to use as an identifier.
46+
pub fn clone_name(name: &str) -> Self {
47+
BufferIdentifier::Name(Cow::Owned(name.to_owned()))
4348
}
4449

45-
/// Get one of the buffers from the map by its name.
46-
pub fn get(&self, name: &str) -> Option<&AnyBuffer> {
47-
self.inner.get(name)
50+
/// Borrow a string literal name to use as an identifier.
51+
pub fn literal_name(name: &'static str) -> Self {
52+
BufferIdentifier::Name(Cow::Borrowed(name))
53+
}
54+
55+
/// Use an index as an identifier.
56+
pub fn index(index: usize) -> Self {
57+
BufferIdentifier::Index(index)
58+
}
59+
}
60+
61+
impl From<&'static str> for BufferIdentifier<'static> {
62+
fn from(value: &'static str) -> Self {
63+
BufferIdentifier::Name(Cow::Borrowed(value))
64+
}
65+
}
66+
67+
pub type BufferMap = HashMap<BufferIdentifier<'static>, AnyBuffer>;
68+
69+
/// Extension trait that makes it more convenient to insert buffers into a [`BufferMap`].
70+
pub trait AddBufferToMap {
71+
/// Convenience function for inserting items into a [`BufferMap`]. This
72+
/// automatically takes care of converting the types.
73+
fn insert_buffer<I: Into<BufferIdentifier<'static>>, B: AsAnyBuffer>(
74+
&mut self,
75+
identifier: I,
76+
buffer: B,
77+
);
78+
}
79+
80+
impl AddBufferToMap for BufferMap {
81+
fn insert_buffer<I: Into<BufferIdentifier<'static>>, B: AsAnyBuffer>(
82+
&mut self,
83+
identifier: I,
84+
buffer: B,
85+
) {
86+
self.insert(identifier.into(), buffer.as_any_buffer());
4887
}
4988
}
5089

@@ -54,56 +93,57 @@ impl BufferMap {
5493
#[error("the incoming buffer map is incompatible with the layout")]
5594
pub struct IncompatibleLayout {
5695
/// Names of buffers that were missing from the incoming buffer map.
57-
pub missing_buffers: Vec<Cow<'static, str>>,
96+
pub missing_buffers: Vec<BufferIdentifier<'static>>,
5897
/// Buffers whose expected type did not match the received type.
5998
pub incompatible_buffers: Vec<BufferIncompatibility>,
6099
}
61100

62101
impl IncompatibleLayout {
63-
/// Check whether a named buffer is compatible with a specific concrete message type.
64-
pub fn require_message_type<Message: 'static>(
102+
/// Check whether a named buffer is compatible with a specialized buffer type,
103+
/// such as `JsonBuffer`.
104+
pub fn require_buffer_by_name<BufferType: 'static>(
65105
&mut self,
66106
expected_name: &str,
67107
buffers: &BufferMap,
68-
) -> Result<Buffer<Message>, ()> {
69-
if let Some((name, buffer)) = buffers.inner.get_key_value(expected_name) {
70-
if let Some(buffer) = buffer.downcast_for_message::<Message>() {
108+
) -> Result<BufferType, ()> {
109+
let identifier = BufferIdentifier::Name(Cow::Borrowed(expected_name));
110+
if let Some(buffer) = buffers.get(&identifier) {
111+
if let Some(buffer) = buffer.downcast_buffer::<BufferType>() {
71112
return Ok(buffer);
72113
} else {
73114
self.incompatible_buffers.push(BufferIncompatibility {
74-
name: name.clone(),
75-
expected: std::any::type_name::<Buffer<Message>>(),
115+
identifier: BufferIdentifier::Name(Cow::Owned(expected_name.to_owned())),
116+
expected: std::any::type_name::<BufferType>(),
76117
received: buffer.message_type_name(),
77118
});
78119
}
79120
} else {
80-
self.missing_buffers
81-
.push(Cow::Owned(expected_name.to_owned()));
121+
self.missing_buffers.push(BufferIdentifier::Name(Cow::Owned(expected_name.to_owned())));
82122
}
83123

84124
Err(())
85125
}
86126

87-
/// Check whether a named buffer is compatible with a specialized buffer type,
88-
/// such as `JsonBuffer`.
89-
pub fn require_buffer_type<BufferType: 'static>(
127+
/// Same as [`Self::require_buffer_by_name`] but more efficient for names
128+
/// given by string literal values.
129+
pub fn require_buffer_by_literal<BufferType: 'static>(
90130
&mut self,
91-
expected_name: &str,
131+
expected_name: &'static str,
92132
buffers: &BufferMap,
93133
) -> Result<BufferType, ()> {
94-
if let Some((name, buffer)) = buffers.inner.get_key_value(expected_name) {
134+
let identifier = BufferIdentifier::Name(Cow::Borrowed(expected_name));
135+
if let Some(buffer) = buffers.get(&identifier) {
95136
if let Some(buffer) = buffer.downcast_buffer::<BufferType>() {
96137
return Ok(buffer);
97138
} else {
98139
self.incompatible_buffers.push(BufferIncompatibility {
99-
name: name.clone(),
140+
identifier,
100141
expected: std::any::type_name::<BufferType>(),
101142
received: buffer.message_type_name(),
102143
});
103144
}
104145
} else {
105-
self.missing_buffers
106-
.push(Cow::Owned(expected_name.to_owned()));
146+
self.missing_buffers.push(identifier);
107147
}
108148

109149
Err(())
@@ -114,7 +154,7 @@ impl IncompatibleLayout {
114154
#[derive(Debug, Clone)]
115155
pub struct BufferIncompatibility {
116156
/// Name of the expected buffer
117-
pub name: Cow<'static, str>,
157+
pub identifier: BufferIdentifier<'static>,
118158
/// The type that was expected for this buffer
119159
pub expected: &'static str,
120160
/// The type that was received for this buffer
@@ -246,36 +286,36 @@ pub trait BufferKeyMap: 'static + Send + Sync + Sized + Clone {
246286

247287
impl BufferMapLayout for BufferMap {
248288
fn buffer_list(&self) -> SmallVec<[AnyBuffer; 8]> {
249-
self.inner.values().cloned().collect()
289+
self.values().cloned().collect()
250290
}
251291
fn try_from_buffer_map(buffers: &BufferMap) -> Result<Self, IncompatibleLayout> {
252292
Ok(buffers.clone())
253293
}
254294
}
255295

256296
impl Joined for BufferMap {
257-
type Item = HashMap<Cow<'static, str>, AnyMessageBox>;
297+
type Item = HashMap<BufferIdentifier<'static>, AnyMessageBox>;
258298

259299
fn pull(&self, session: Entity, world: &mut World) -> Result<Self::Item, OperationError> {
260300
let mut value = HashMap::new();
261-
for (name, buffer) in &self.inner {
301+
for (name, buffer) in self.iter() {
262302
value.insert(name.clone(), buffer.pull(session, world)?);
263303
}
264304

265305
Ok(value)
266306
}
267307
}
268308

269-
impl JoinedValue for HashMap<Cow<'static, str>, AnyMessageBox> {
309+
impl JoinedValue for HashMap<BufferIdentifier<'static>, AnyMessageBox> {
270310
type Buffers = BufferMap;
271311
}
272312

273313
impl Accessed for BufferMap {
274-
type Key = HashMap<Cow<'static, str>, AnyBufferKey>;
314+
type Key = HashMap<BufferIdentifier<'static>, AnyBufferKey>;
275315

276316
fn create_key(&self, builder: &BufferKeyBuilder) -> Self::Key {
277317
let mut keys = HashMap::new();
278-
for (name, buffer) in &self.inner {
318+
for (name, buffer) in self.iter() {
279319
let key = AnyBufferKey {
280320
tag: builder.make_tag(buffer.id()),
281321
interface: buffer.interface,
@@ -286,7 +326,7 @@ impl Accessed for BufferMap {
286326
}
287327

288328
fn add_accessor(&self, accessor: Entity, world: &mut World) -> OperationResult {
289-
for buffer in self.inner.values() {
329+
for buffer in self.values() {
290330
buffer.add_accessor(accessor, world)?;
291331
}
292332
Ok(())
@@ -313,7 +353,7 @@ impl Accessed for BufferMap {
313353

314354
#[cfg(test)]
315355
mod tests {
316-
use crate::{prelude::*, testing::*, BufferMap};
356+
use crate::{prelude::*, testing::*, BufferMap, AddBufferToMap};
317357

318358
#[derive(JoinedValue)]
319359
struct TestJoinedValue<T: Send + Sync + 'static + Clone> {
@@ -337,11 +377,11 @@ mod tests {
337377
let buffer_any = builder.create_buffer(BufferSettings::default());
338378

339379
let mut buffers = BufferMap::default();
340-
buffers.insert("integer", buffer_i64);
341-
buffers.insert("float", buffer_f64);
342-
buffers.insert("string", buffer_string);
343-
buffers.insert("generic", buffer_generic);
344-
buffers.insert("any", buffer_any);
380+
buffers.insert_buffer("integer", buffer_i64);
381+
buffers.insert_buffer("float", buffer_f64);
382+
buffers.insert_buffer("string", buffer_string);
383+
buffers.insert_buffer("generic", buffer_generic);
384+
buffers.insert_buffer("any", buffer_any);
345385

346386
scope.input.chain(builder).fork_unzip((
347387
|chain: Chain<_>| chain.connect(buffer_i64.input_slot()),

src/buffer/json_buffer.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use smallvec::SmallVec;
3737

3838
use crate::{
3939
add_listener_to_source, Accessed, AnyBuffer, AnyBufferAccessInterface, AnyBufferKey, AnyRange,
40-
Buffer, BufferAccessMut, BufferAccessors, BufferError, BufferKey, BufferKeyBuilder,
40+
AsAnyBuffer, Buffer, BufferAccessMut, BufferAccessors, BufferError, BufferKey, BufferKeyBuilder,
4141
BufferKeyTag, BufferLocation, BufferStorage, Bufferable, Buffered, Builder, DrainBuffer, Gate,
4242
GateState, InspectBuffer, Joined, ManageBuffer, NotifyBufferUpdate, OperationError,
4343
OperationResult, OrBroken,
@@ -72,11 +72,6 @@ impl JsonBuffer {
7272
self.as_any_buffer().downcast_buffer::<BufferType>()
7373
}
7474

75-
/// Cast this into an [`AnyBuffer`].
76-
pub fn as_any_buffer(&self) -> AnyBuffer {
77-
self.clone().into()
78-
}
79-
8075
/// Register the ability to cast into [`JsonBuffer`] and [`JsonBufferKey`]
8176
/// for buffers containing messages of type `T`. This only needs to be done
8277
/// once in the entire lifespan of a program.
@@ -127,6 +122,12 @@ impl From<JsonBuffer> for AnyBuffer {
127122
}
128123
}
129124

125+
impl AsAnyBuffer for JsonBuffer {
126+
fn as_any_buffer(&self) -> AnyBuffer {
127+
(*self).into()
128+
}
129+
}
130+
130131
/// Similar to a [`BufferKey`] except it can be used for any buffer that supports
131132
/// serialization and deserialization without knowing the buffer's specific
132133
/// message type at compile time.
@@ -1045,7 +1046,7 @@ impl Accessed for JsonBuffer {
10451046

10461047
#[cfg(test)]
10471048
mod tests {
1048-
use crate::{prelude::*, testing::*};
1049+
use crate::{prelude::*, testing::*, AddBufferToMap};
10491050
use bevy_ecs::prelude::World;
10501051
use serde::{Deserialize, Serialize};
10511052

@@ -1374,9 +1375,9 @@ mod tests {
13741375
let buffer_json = builder.create_buffer(BufferSettings::default());
13751376

13761377
let mut buffers = BufferMap::default();
1377-
buffers.insert("integer", buffer_i64);
1378-
buffers.insert("float", buffer_f64);
1379-
buffers.insert("json", buffer_json);
1378+
buffers.insert_buffer("integer", buffer_i64);
1379+
buffers.insert_buffer("float", buffer_f64);
1380+
buffers.insert_buffer("json", buffer_json);
13801381

13811382
scope.input.chain(builder).fork_unzip((
13821383
|chain: Chain<_>| chain.connect(buffer_i64.input_slot()),

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ pub mod prelude {
339339
pub use crate::{
340340
buffer::{
341341
Accessible, AnyBuffer, AnyBufferKey, AnyBufferMut, AnyBufferWorldAccess, AnyMessageBox,
342-
Buffer, BufferAccess, BufferAccessMut, BufferKey, BufferKeyMap, BufferMap,
342+
AsAnyBuffer, Buffer, BufferAccess, BufferAccessMut, BufferKey, BufferKeyMap, BufferMap,
343343
BufferMapLayout, BufferSettings, Bufferable, Buffered, IncompatibleLayout,
344344
IterBufferable, Joinable, JoinedValue, RetentionPolicy,
345345
},

0 commit comments

Comments
 (0)