use crate::{IDBoundAlloc, IEntityAllocID, IPoliciedID};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EntityListNodeHead<I: IPoliciedID> {
pub prev: Option<I>,
pub next: Option<I>,
}
impl<I: IPoliciedID> EntityListNodeHead<I> {
pub fn none() -> Self {
Self {
prev: None,
next: None,
}
}
pub fn new_full(prev: Option<I>, next: Option<I>) -> Self {
Self { prev, next }
}
pub fn from_id(prev: I, next: I) -> Self {
Self {
prev: Some(prev),
next: Some(next),
}
}
pub fn into_id(self) -> (Option<I>, Option<I>) {
(self.prev, self.next)
}
pub fn get_prev(&self) -> Option<I> {
self.prev
}
pub fn get_next(&self) -> Option<I> {
self.next
}
fn prev(mut self, prev: Option<I>) -> Self {
self.prev = prev;
self
}
fn next(mut self, next: Option<I>) -> Self {
self.next = next;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EntityListError<I: IPoliciedID> {
EmptyList,
ListBroken,
NodeIsSentinel,
RepeatedNode,
RepeatedList,
SelfNotAttached(I),
ItemFalselyAttached(I),
ItemFalselyDetached(I),
}
impl<I: IPoliciedID> std::fmt::Display for EntityListError<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(&self, f)
}
}
impl<I: IPoliciedID> std::error::Error for EntityListError<I> {}
pub type EntityListRes<I, T = ()> = Result<T, EntityListError<I>>;
pub trait IBasicEntityListID: IPoliciedID {
fn obj_load_head(obj: &Self::ObjectT) -> EntityListNodeHead<Self>;
fn obj_store_head(obj: &Self::ObjectT, head: EntityListNodeHead<Self>);
fn obj_is_sentinel(obj: &Self::ObjectT) -> bool;
fn new_sentinel_obj() -> Self::ObjectT;
#[inline]
fn is_sentinel(self, alloc: &IDBoundAlloc<Self>) -> bool {
Self::obj_is_sentinel(self.deref_alloc(alloc))
}
fn new_sentinel(alloc: &IDBoundAlloc<Self>) -> Self {
let obj = Self::new_sentinel_obj();
Self::from_backend(Self::BackID::allocate_from(alloc, obj))
}
fn on_push_prev(self, prev: Self, alloc: &IDBoundAlloc<Self>) -> EntityListRes<Self>;
fn on_push_next(self, next: Self, alloc: &IDBoundAlloc<Self>) -> EntityListRes<Self>;
fn on_unplug(self, alloc: &IDBoundAlloc<Self>) -> EntityListRes<Self>;
fn get_prev_id(self, alloc: &IDBoundAlloc<Self>) -> Option<Self> {
Self::obj_get_prev_id(self.deref_alloc(alloc))
}
fn get_next_id(self, alloc: &IDBoundAlloc<Self>) -> Option<Self> {
Self::obj_get_next_id(self.deref_alloc(alloc))
}
fn is_attached(self, alloc: &IDBoundAlloc<Self>) -> bool {
Self::obj_is_attached(self.deref_alloc(alloc))
}
}
pub(super) trait NodeOps: IPoliciedID {
fn obj_get_prev_id(obj: &Self::ObjectT) -> Option<Self>;
fn obj_get_next_id(obj: &Self::ObjectT) -> Option<Self>;
fn obj_set_prev_id(obj: &Self::ObjectT, prev: Option<Self>);
fn obj_set_next_id(obj: &Self::ObjectT, next: Option<Self>);
fn obj_is_attached(obj: &Self::ObjectT) -> bool;
fn set_prev_id(self, alloc: &IDBoundAlloc<Self>, prev: Option<Self>) {
Self::obj_set_prev_id(self.deref_alloc(alloc), prev);
}
fn set_next_id(self, alloc: &IDBoundAlloc<Self>, next: Option<Self>) {
Self::obj_set_next_id(self.deref_alloc(alloc), next);
}
}
impl<I: IBasicEntityListID> NodeOps for I {
fn obj_get_prev_id(obj: &Self::ObjectT) -> Option<Self> {
Self::obj_load_head(obj).prev
}
fn obj_get_next_id(obj: &Self::ObjectT) -> Option<Self> {
Self::obj_load_head(obj).next
}
fn obj_set_prev_id(obj: &Self::ObjectT, prev: Option<Self>) {
let head = Self::obj_load_head(obj);
Self::obj_store_head(obj, head.prev(prev));
}
fn obj_set_next_id(obj: &Self::ObjectT, next: Option<Self>) {
let head = Self::obj_load_head(obj);
Self::obj_store_head(obj, head.next(next));
}
fn obj_is_attached(obj: &Self::ObjectT) -> bool {
let head = Self::obj_load_head(obj);
let is_sentinel = Self::obj_is_sentinel(obj);
let has_prev = head.prev.is_some();
let has_next = head.next.is_some();
if cfg!(debug_assertions) && !is_sentinel && has_prev != has_next {
panic!("EntityListNodeID: obj_is_attached: inconsistent attachment state detected");
}
has_prev || has_next
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EntityListRange<I: IBasicEntityListID> {
pub start: I,
pub end: Option<I>,
}
impl<I: IBasicEntityListID> EntityListRange<I> {
pub fn is_empty(&self) -> bool {
Some(self.start) == self.end
}
pub fn iter<'a>(&self, alloc: &'a IDBoundAlloc<I>) -> EntityListIter<'a, I> {
EntityListIter::new(*self, alloc)
}
pub fn count_len(&self, alloc: &IDBoundAlloc<I>) -> usize {
self.iter(alloc).count()
}
pub fn debug<'a>(&self, alloc: &'a IDBoundAlloc<I>) -> EntityListRangeDebug<'a, I> {
EntityListRangeDebug {
alloc,
range: *self,
}
}
}
pub struct EntityListIter<'alloc, I: IBasicEntityListID> {
pub(super) alloc: &'alloc IDBoundAlloc<I>,
pub(super) curr: Option<I>,
pub(super) end: Option<I>,
}
impl<'alloc, I: IBasicEntityListID> Iterator for EntityListIter<'alloc, I>
where
I::ObjectT: 'alloc,
{
type Item = (I, &'alloc I::ObjectT);
fn next(&mut self) -> Option<Self::Item> {
let curr = self.curr?;
if Some(curr) == self.end {
return None;
}
let obj = curr.deref_alloc(self.alloc);
self.curr = curr.get_next_id(self.alloc);
Some((curr, obj))
}
}
impl<'alloc, I: IBasicEntityListID> EntityListIter<'alloc, I> {
pub fn new(range: EntityListRange<I>, alloc: &'alloc IDBoundAlloc<I>) -> Self {
Self {
alloc,
curr: Some(range.start),
end: range.end,
}
}
}
pub struct EntityListRangeDebug<'a, I: IBasicEntityListID> {
pub alloc: &'a IDBoundAlloc<I>,
pub range: EntityListRange<I>,
}
impl<'a, I> std::fmt::Debug for EntityListRangeDebug<'a, I>
where
I: IBasicEntityListID,
I::ObjectT: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut list_dbg = f.debug_list();
for (_id, obj) in self.range.iter(self.alloc) {
list_dbg.entry(obj);
}
list_dbg.finish()
}
}