use core::mem::transmute;
#[const_trait]
pub trait Bag: private::Sealed + Copy {
#[inline]
fn insert<T: 'static>(self, head: T) -> List<T, Self> {
assert!(self.get::<T>().is_none(), "duplicate entry");
(head, self)
}
fn get<T: 'static>(&self) -> Option<&T>;
fn get_mut<T: 'static>(&mut self) -> Option<&mut T>;
}
pub const EMPTY: Empty = ();
pub type Empty = ();
pub type List<Head, Tail> = (Head, Tail);
#[doc(no_inline)]
pub use either::Either;
impl const Bag for Empty {
fn get<T: 'static>(&self) -> Option<&T> {
None
}
fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
None
}
}
impl<Head: 'static + Copy, Tail: ~const Bag> const Bag for List<Head, Tail> {
fn get<T: 'static>(&self) -> Option<&T> {
if TypeId::of::<T>().eq(&TypeId::of::<Head>()) {
Some(unsafe { transmute::<&Head, &T>(&self.0) })
} else {
self.1.get()
}
}
fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
if TypeId::of::<T>().eq(&TypeId::of::<Head>()) {
Some(unsafe { transmute::<&mut Head, &mut T>(&mut self.0) })
} else {
self.1.get_mut()
}
}
}
impl<Left: ~const Bag, Right: ~const Bag> const Bag for Either<Left, Right> {
fn get<T: 'static>(&self) -> Option<&T> {
match self {
Either::Left(x) => x.get(),
Either::Right(x) => x.get(),
}
}
fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
match self {
Either::Left(x) => x.get_mut(),
Either::Right(x) => x.get_mut(),
}
}
}
mod private {
use super::Bag;
#[const_trait]
pub trait Sealed {}
impl const Sealed for () {}
impl<Head: 'static, Tail: ~const Bag> const Sealed for super::List<Head, Tail> {}
impl<Left: ~const Bag, Right: ~const Bag> const Sealed for super::Either<Left, Right> {}
}
struct TypeId {
inner: core::any::TypeId,
}
impl TypeId {
#[inline]
const fn of<T: 'static>() -> Self {
Self {
inner: core::any::TypeId::of::<T>(),
}
}
#[inline]
const fn eq(&self, other: &Self) -> bool {
unsafe {
type TypeIdBytes = [u8; core::mem::size_of::<core::any::TypeId>()];
let x: TypeIdBytes = transmute(self.inner);
let y: TypeIdBytes = transmute(other.inner);
let mut i = 0;
while i < x.len() {
if x[i] != y[i] {
return false;
}
i += 1;
}
true
}
}
}