use std::any::Any;
use crate::engine::error::RegistryResult;
use crate::engine::types::ComponentID;
use super::signature::Signature;
pub trait DynamicBundle {
fn take(&mut self, component_id: ComponentID) -> Option<Box<dyn Any + Send>>;
}
#[derive(Default)]
pub struct Bundle {
signature: Signature,
values: Vec<(ComponentID, Box<dyn Any + Send>)>,
}
impl Bundle {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn clear(&mut self) {
self.signature = Signature::default();
self.values.clear();
}
#[inline]
pub fn insert<T: Any + Send>(&mut self, component_id: ComponentID, value: T) {
self.signature.set(component_id);
if let Some((_, slot)) = self.values.iter_mut().find(|(cid, _)| *cid == component_id) {
*slot = Box::new(value);
} else {
self.values.push((component_id, Box::new(value)));
}
}
#[inline]
pub fn try_insert<T: Any + Send>(
&mut self,
component_id: ComponentID,
value: T,
) -> RegistryResult<()> {
self.signature.try_set(component_id)?;
if let Some((_, slot)) = self.values.iter_mut().find(|(cid, _)| *cid == component_id) {
*slot = Box::new(value);
} else {
self.values.push((component_id, Box::new(value)));
}
Ok(())
}
#[inline]
pub fn extend_from_iter<T: Any + Send, I: IntoIterator<Item = (ComponentID, T)>>(
&mut self,
iter: I,
) {
for (component_id, value) in iter {
self.insert(component_id, value);
}
}
#[inline]
pub fn is_complete_for(&self, required: &Signature) -> bool {
required
.iterate_over_components()
.all(|cid| self.signature.has(cid))
}
#[inline]
pub fn signature(&self) -> Signature {
self.signature
}
pub fn insert_boxed(&mut self, id: ComponentID, value: Box<dyn Any + Send>) {
self.signature.set(id);
if let Some((_, slot)) = self.values.iter_mut().find(|(cid, _)| *cid == id) {
*slot = value;
} else {
self.values.push((id, value));
}
}
pub fn try_insert_boxed(
&mut self,
id: ComponentID,
value: Box<dyn Any + Send>,
) -> RegistryResult<()> {
self.signature.try_set(id)?;
if let Some((_, slot)) = self.values.iter_mut().find(|(cid, _)| *cid == id) {
*slot = value;
} else {
self.values.push((id, value));
}
Ok(())
}
}
impl DynamicBundle for Bundle {
#[inline]
fn take(&mut self, component_id: ComponentID) -> Option<Box<dyn Any + Send>> {
let index = self
.values
.iter()
.position(|(cid, _)| *cid == component_id)?;
let (_, value) = self.values.swap_remove(index);
Some(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::engine::error::RegistryError;
use crate::engine::types::COMPONENT_CAP;
#[test]
fn fallible_insert_helpers_reject_out_of_range_component_id() {
let invalid = COMPONENT_CAP as ComponentID;
let mut bundle = Bundle::new();
assert!(matches!(
bundle.try_insert(invalid, 1_u32),
Err(RegistryError::InvalidComponentId { .. })
));
assert!(matches!(
bundle.try_insert_boxed(invalid, Box::new(2_u32)),
Err(RegistryError::InvalidComponentId { .. })
));
}
}