use std::any::Any;
use std::collections::HashSet;
use std::marker::PhantomData;
use std::{iter, ops};
use self::search_single::SearchSingleStrong;
use super::Raw;
use crate::util::DbgTypeId;
use crate::Archetype;
pub(crate) mod search_single;
mod std_impl;
pub trait Referrer: 'static {
fn visit_type(arg: &mut VisitTypeArg);
fn visit_mut<V: VisitMutArg>(&mut self, arg: &mut V);
}
pub struct VisitTypeArg<'t> {
recursion_guard: HashSet<DbgTypeId>,
pub(crate) found_archs: HashSet<DbgTypeId>,
_ph: PhantomData<&'t ()>,
}
impl<'t> VisitTypeArg<'t> {
pub(crate) fn new() -> Self {
Self {
recursion_guard: HashSet::new(),
found_archs: HashSet::new(),
_ph: PhantomData,
}
}
pub fn mark<T: 'static>(&mut self) -> ops::ControlFlow<(), ()> {
if self.recursion_guard.insert(DbgTypeId::of::<T>()) {
ops::ControlFlow::Continue(())
} else {
ops::ControlFlow::Break(())
}
}
fn add_archetype<A: Archetype>(&mut self) { self.found_archs.insert(DbgTypeId::of::<A>()); }
}
pub(crate) mod sealed {
pub trait Sealed {}
}
pub trait VisitMutArg: sealed::Sealed {
#[doc(hidden)]
fn _visit_strong(&mut self, args: VisitStrongArgs) -> VisitStrongResult;
#[doc(hidden)]
fn _visit_weak(&mut self, args: VisitWeakArgs) -> VisitWeakResult;
#[doc(hidden)]
fn _set_debug_name(&mut self, _name: String) {}
}
#[doc(hidden)]
pub struct VisitStrongArgs<'t> {
archetype: DbgTypeId,
raw: usize,
rc: &'t mut super::MaybeArc,
}
#[doc(hidden)]
pub struct VisitStrongResult {
new_raw: usize,
}
#[doc(hidden)]
pub struct VisitWeakArgs<'t> {
archetype: DbgTypeId,
raw: usize,
rc: &'t mut super::MaybeWeak,
}
#[doc(hidden)]
pub struct VisitWeakResult {
new_raw: usize,
}
impl<A: Archetype> Referrer for super::Entity<A> {
#[inline]
fn visit_type(arg: &mut VisitTypeArg) {
if arg.mark::<Self>().is_break() {
return;
}
arg.add_archetype::<A>();
}
#[inline]
fn visit_mut<V: VisitMutArg>(&mut self, arg: &mut V) {
let ret = arg._visit_strong(VisitStrongArgs {
archetype: DbgTypeId::of::<A>(),
raw: self.id.to_primitive(),
rc: &mut self.rc,
});
self.id = A::RawEntity::from_primitive(ret.new_raw);
}
}
impl<A: Archetype> Referrer for super::Weak<A> {
#[inline]
fn visit_type(arg: &mut VisitTypeArg) { arg.mark::<Self>(); }
#[inline]
fn visit_mut<V: VisitMutArg>(&mut self, arg: &mut V) {
let ret = arg._visit_weak(VisitWeakArgs {
archetype: DbgTypeId::of::<A>(),
raw: self.id.to_primitive(),
rc: &mut self.rc,
});
self.id = A::RawEntity::from_primitive(ret.new_raw);
}
}
pub struct AsObject<'t>(pub(crate) Box<dyn Object + 't>);
impl<'t> AsObject<'t> {
pub fn of(value: &'t mut impl Referrer) -> Self {
Self(Box::new(UnnamedIter(iter::once(value))))
}
}
pub(crate) trait Object {
fn search_single_strong(&mut self, state: &mut SearchSingleStrong);
}
pub(crate) struct SingleVtable {
search_single_strong: fn(&mut dyn Any, &mut SearchSingleStrong),
}
impl SingleVtable {
pub(crate) fn of<T: Referrer>() -> Self {
Self {
search_single_strong: |object, state| {
object.downcast_mut::<T>().expect("TypeId mismatch").visit_mut(state)
},
}
}
pub(crate) fn search_single_strong(
&mut self,
value: &mut dyn Any,
state: &mut SearchSingleStrong,
) {
(self.search_single_strong)(value, state)
}
}
#[repr(transparent)]
pub(crate) struct UnnamedIter<I>(pub(crate) I);
impl<I: Iterator> Object for UnnamedIter<I>
where
<I as Iterator>::Item: ops::DerefMut,
<<I as Iterator>::Item as ops::Deref>::Target: Referrer,
{
fn search_single_strong(&mut self, state: &mut SearchSingleStrong) {
for mut item in self.0.by_ref() {
let item = &mut *item;
item.visit_mut(state);
}
}
}
pub(crate) struct NamedIter<I>(pub(crate) I);
impl<T: Object, I: Iterator<Item = (Option<String>, T)>> Object for NamedIter<I> {
fn search_single_strong(&mut self, state: &mut SearchSingleStrong) {
for (debug_name, mut item) in self.0.by_ref() {
if let Some(name) = debug_name {
state._set_debug_name(name);
}
item.search_single_strong(state);
}
}
}
pub(crate) struct NamedBoxIter<I>(pub(crate) I);
impl<'t, I: Iterator<Item = (Option<String>, Box<dyn Object + 't>)>> Object for NamedBoxIter<I> {
fn search_single_strong(&mut self, state: &mut SearchSingleStrong) {
for (debug_name, mut item) in self.0.by_ref() {
if let Some(name) = debug_name {
state._set_debug_name(name);
}
item.search_single_strong(state);
}
}
}