waffle/src/entity.rs
2023-02-24 21:28:40 -08:00

181 lines
5 KiB
Rust

//! Type-safe indices and indexed containers.
use std::default::Default;
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
use std::ops::{Index, IndexMut};
pub trait EntityRef: Clone + Copy + PartialEq + Eq + PartialOrd + Ord + Hash {
fn new(value: usize) -> Self;
fn index(self) -> usize;
fn invalid() -> Self;
fn is_valid(self) -> bool {
self != Self::invalid()
}
fn is_invalid(self) -> bool {
self == Self::invalid()
}
fn maybe_index(self) -> Option<usize>;
}
#[macro_export]
macro_rules! declare_entity {
($name:tt, $prefix:tt) => {
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct $name(u32);
impl $crate::entity::EntityRef for $name {
fn new(value: usize) -> Self {
use std::convert::TryFrom;
let value = u32::try_from(value).unwrap();
debug_assert!(value != u32::MAX);
Self(value)
}
fn index(self) -> usize {
debug_assert!(self.is_valid());
self.0 as usize
}
fn maybe_index(self) -> Option<usize> {
if self.is_valid() {
Some(self.0 as usize)
} else {
None
}
}
fn invalid() -> Self {
Self(u32::MAX)
}
}
impl std::convert::From<u32> for $name {
fn from(val: u32) -> Self {
<Self as $crate::entity::EntityRef>::new(val as usize)
}
}
impl std::default::Default for $name {
fn default() -> Self {
<Self as $crate::entity::EntityRef>::invalid()
}
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}{}", $prefix, self.0)
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}{}", $prefix, self.0)
}
}
};
}
#[derive(Clone, Debug)]
pub struct EntityVec<Idx: EntityRef, T: Clone + Debug>(Vec<T>, PhantomData<Idx>);
impl<Idx: EntityRef, T: Clone + Debug> std::default::Default for EntityVec<Idx, T> {
fn default() -> Self {
Self(vec![], PhantomData)
}
}
impl<Idx: EntityRef, T: Clone + Debug> From<Vec<T>> for EntityVec<Idx, T> {
fn from(vec: Vec<T>) -> Self {
Self(vec, PhantomData)
}
}
impl<Idx: EntityRef, T: Clone + Debug> EntityVec<Idx, T> {
pub fn push(&mut self, t: T) -> Idx {
let idx = Idx::new(self.0.len());
self.0.push(t);
idx
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn iter(&self) -> impl DoubleEndedIterator<Item = Idx> {
(0..self.0.len()).map(|index| Idx::new(index))
}
pub fn values(&self) -> impl DoubleEndedIterator<Item = &T> {
self.0.iter()
}
pub fn values_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut T> {
self.0.iter_mut()
}
pub fn entries(&self) -> impl DoubleEndedIterator<Item = (Idx, &T)> {
self.0
.iter()
.enumerate()
.map(|(index, t)| (Idx::new(index), t))
}
pub fn entries_mut(&mut self) -> impl Iterator<Item = (Idx, &mut T)> {
self.0
.iter_mut()
.enumerate()
.map(|(index, t)| (Idx::new(index), t))
}
pub fn get(&self, idx: Idx) -> Option<&T> {
self.0.get(idx.index())
}
pub fn get_mut(&mut self, idx: Idx) -> Option<&mut T> {
self.0.get_mut(idx.index())
}
pub fn into_vec(self) -> Vec<T> {
self.0
}
}
impl<Idx: EntityRef, T: Clone + Debug> Index<Idx> for EntityVec<Idx, T> {
type Output = T;
fn index(&self, idx: Idx) -> &T {
&self.0[idx.index()]
}
}
impl<Idx: EntityRef, T: Clone + Debug> IndexMut<Idx> for EntityVec<Idx, T> {
fn index_mut(&mut self, idx: Idx) -> &mut T {
&mut self.0[idx.index()]
}
}
#[derive(Clone, Debug, Default)]
pub struct PerEntity<Idx: EntityRef, T: Clone + Debug + Default>(Vec<T>, PhantomData<Idx>, T);
impl<Idx: EntityRef, T: Clone + Debug + Default> Index<Idx> for PerEntity<Idx, T> {
type Output = T;
fn index(&self, idx: Idx) -> &T {
debug_assert!(idx.is_valid());
self.0.get(idx.index()).unwrap_or(&self.2)
}
}
impl<Idx: EntityRef, T: Clone + Debug + Default> IndexMut<Idx> for PerEntity<Idx, T> {
fn index_mut(&mut self, idx: Idx) -> &mut T {
debug_assert!(idx.is_valid());
if idx.index() >= self.0.len() {
self.0.resize(idx.index() + 1, T::default());
}
&mut self.0[idx.index()]
}
}
impl<Idx: EntityRef, T: Clone + Debug + Default + PartialEq> PartialEq for PerEntity<Idx, T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<Idx: EntityRef, T: Clone + Debug + Default + PartialEq + Eq> Eq for PerEntity<Idx, T> {}