use std::fmt::Debug;
use std::ops::Deref;
mod cow;
mod insert;
mod r#mut;
pub use cow::{CopyOnWriteComponentCollection, CopyOnWriteComponentRef};
pub use insert::{InsertOptimizedComponentCollection, InsertOptimizedComponentRef};
pub use r#mut::{MutableComponentCollection, MutableComponentRef};
use crate::partitioning::PartitioningScheme;
use crate::Entity;
pub trait ComponentCollection<E: Entity, T: Debug>:
Debug + Default + FromIterator<(E, T)> + FromIterator<(E, ComponentChange<T>)>
{
type Ref<'a>: ComponentRef<T>
where
Self: 'a,
T: 'a;
type Consumed: Iterator<Item = (E, T)>;
fn is_empty(&self) -> bool;
fn len(&self) -> usize;
fn lower_bound(&self, lower_bound: E) -> Option<E>;
fn get_ref(&self, entity: E) -> Option<Self::Ref<'_>>;
fn consume(self) -> Self::Consumed;
fn partition(self, partitioning: &dyn PartitioningScheme<E>) -> Vec<Option<Self>> {
let mut consumed = self.consume();
let mut consume_next = consumed.next();
let mut partition = 0usize;
let mut partitions = Vec::with_capacity(partitioning.len() + 1);
let mut current_partition = vec![];
while partition < partitioning.len() && consume_next.is_some() {
let target = partitioning.partition(partition);
let c = consume_next.as_ref().unwrap();
if c.0 < target {
current_partition.push(consume_next.unwrap());
consume_next = consumed.next();
} else {
if !current_partition.is_empty() {
partitions.push(Some(Self::from_iter(current_partition)));
} else {
partitions.push(None);
}
current_partition = vec![];
partition += 1;
}
}
while let Some(c) = consume_next {
current_partition.push(c);
consume_next = consumed.next();
}
partitions.push(Some(Self::from_iter(current_partition)));
while partition < partitioning.len() {
partitions.push(None);
partition += 1;
}
assert_eq!(partitioning.len() + 1, partitions.len());
partitions
}
fn apply(&mut self, changes: Vec<(E, ComponentChange<T>)>) {
let this = std::mem::take(self);
*self = apply_component_changes(this, changes.into_iter());
}
}
pub(crate) fn apply_component_changes<
E: Entity,
T: Debug,
C: ComponentCollection<E, T>,
I: Iterator<Item = (E, ComponentChange<T>)>,
>(
collection: C,
mut changes: I,
) -> C {
let mut changes_next = changes.next();
if changes_next.is_none() {
return collection;
}
let mut collected = Vec::with_capacity(collection.len());
let mut collection = collection.consume();
let mut collection_next = collection.next();
while let (Some(c), Some(i)) = (collection_next.as_ref(), changes_next.as_ref()) {
#[allow(clippy::comparison_chain)]
if c.0 == i.0 {
match &i.1 {
ComponentChange::NoChange => {
collected.push(collection_next.unwrap());
}
ComponentChange::Unbind => {
}
ComponentChange::Value(_) => {
let (e, ComponentChange::Value(v)) = changes_next.unwrap() else {
unreachable!();
};
collected.push((e, v));
}
}
collection_next = collection.next();
changes_next = changes.next();
} else if c.0 < i.0 {
collected.push(collection_next.unwrap());
collection_next = collection.next();
} else {
match &i.1 {
ComponentChange::NoChange => {
}
ComponentChange::Unbind => {
}
ComponentChange::Value(_) => {
let (e, ComponentChange::Value(v)) = changes_next.unwrap() else {
unreachable!();
};
collected.push((e, v));
}
}
changes_next = changes.next();
}
}
while collection_next.as_ref().is_some() {
collected.push(collection_next.unwrap());
collection_next = collection.next();
}
while let Some(i) = changes_next.as_ref() {
match &i.1 {
ComponentChange::NoChange => {
}
ComponentChange::Unbind => {
}
ComponentChange::Value(_) => {
let (e, ComponentChange::Value(v)) = changes_next.unwrap() else {
unreachable!();
};
collected.push((e, v));
}
}
changes_next = changes.next();
}
C::from_iter(collected)
}
pub enum ComponentChange<T: Debug> {
NoChange,
Unbind,
Value(T),
}
impl<T: Debug> ComponentChange<T> {
pub fn is_no_change(&self) -> bool {
matches!(self, Self::NoChange)
}
}
pub trait ComponentRef<T: Debug>: Deref<Target = T> + Debug {
fn unbind(&mut self);
fn update<F: FnOnce(&mut T) -> U, U>(&mut self, f: F) -> U;
fn change(self) -> ComponentChange<T>;
}
#[cfg(test)]
pub mod tests {
extern crate proptest;
use std::fmt::Debug;
use proptest::strategy::Strategy;
use super::ComponentCollection;
use crate::tests::{arb_entity, is_free_of_duplicates};
use crate::Entity;
proptest::prop_compose! {
pub fn arb_entities()(mut entities in proptest::collection::vec(arb_entity(), 0..=65536).prop_filter("dedupe", is_free_of_duplicates)) -> Vec<(u128, usize)> {
entities.sort();
entities.dedup();
entities.into_iter().enumerate().map(|(i, x)| (x, i)).collect()
}
}
pub fn collection_properties<E: Entity, T: Debug + Clone + Eq, C: ComponentCollection<E, T>>(
collection: Vec<(E, T)>,
) {
let components = C::from_iter(collection.clone());
assert_eq!(collection.is_empty(), components.is_empty());
assert_eq!(collection.len(), components.len());
for (idx, (e, t)) in collection.iter().enumerate() {
assert_eq!(Some(*e), components.lower_bound(*e));
assert_eq!(*t, *components.get_ref(*e).unwrap());
if idx > 0 && collection[idx - 1].0.increment() != collection[idx].0 {
assert_eq!(Some(*e), components.lower_bound(e.decrement()));
assert!(components.get_ref(e.decrement()).is_none());
}
}
let consumed: Vec<(E, T)> = components.consume().collect();
assert_eq!(collection, consumed);
}
}