use std::{borrow::BorrowMut, marker::PhantomData};
use crate::{lifetime::STATIC_REGION, with_region, Region, Storable};
#[inline]
pub fn with_type<U, Z>(f: impl for <'c> FnOnce(UniqueType<'c, U>) -> Z) -> Z
{
with_region(|region| f(UniqueType(region, PhantomData)))
}
#[inline]
pub fn try_with_types<Types: TryGenTuple, Z>(f: impl for <'c> FnOnce(Region<'c>, Types::Tuple<'c>) -> Z) -> Option<Z>
{
with_region(|region| Types::try_gen_tuple(region).map(|types| f(region, types)))
}
#[inline]
pub fn with_types<Types: TryGenTuple, Z>(f: impl for <'c> FnOnce(Region<'c>, Types::Tuple<'c>) -> Z) -> Z
{
try_with_types(f).unwrap()
}
#[repr(transparent)]
pub struct Gen<Z>(Z);
impl<Z: Storable> Gen<Z>
{
#[inline]
fn from_fn(f: impl for <'c> FnOnce(Region<'c>) -> Z::Generative<'c>) -> Self
{
Gen(f(STATIC_REGION).into())
}
#[inline]
fn try_from_fn(f: impl for <'c> FnOnce(Region<'c>) -> Option<Z::Generative<'c>>) -> Option<Self>
{
f(STATIC_REGION).map(|inner| Gen(inner.into()))
}
#[inline]
pub fn from_type<U>(f: impl for <'c> FnOnce(UniqueType<'c, U>) -> Z::Generative<'c>) -> Self
{
Self::from_fn(|region| f(UniqueType(region, PhantomData)))
}
#[inline]
pub fn try_from_types<Types: TryGenTuple>(f: impl for <'c> FnOnce(Region<'c>, Types::Tuple<'c>) -> Z::Generative<'c>) -> Option<Self>
{
Self::try_from_fn(|region| Types::try_gen_tuple(region).map(|types| f(region, types)))
}
#[inline]
pub fn from_types<Types: TryGenTuple>(f: impl for <'c> FnOnce(Region<'c>, Types::Tuple<'c>) -> Z::Generative<'c>) -> Self
{
Self::try_from_types(f).unwrap()
}
#[inline]
pub fn with<R>(self, f: impl for <'c> FnOnce(Z::Generative<'c>) -> R) -> R
{
f(self.0.into())
}
#[inline]
pub fn with_ref<R>(&self, f: impl for <'c> FnOnce(&Z::Generative<'c>) -> R) -> R
{
f(self.0.borrow())
}
#[inline]
pub fn with_mut<R>(&mut self, f: impl for <'c> FnOnce(&mut Z::Generative<'c>) -> R) -> R
{
f(self.0.borrow_mut())
}
}
#[repr(transparent)]
pub struct UniqueType<'c, T>(Region<'c>, PhantomData<T>);
impl<'c, T> From<UniqueType<'c, T>> for Region<'c>
{
#[inline]
fn from(value: UniqueType<'c, T>) -> Self {
value.0
}
}
pub trait StaticTuple
{
fn distinct() -> bool;
}
pub trait TryGenTuple: StaticTuple
{
type Tuple<'c>;
fn try_gen_tuple<'c>(region: Region<'c>) -> Option<Self::Tuple<'c>>;
}
macro_rules! gen_tuple {
($($tt:ident),+) => {
impl<$($tt),+> StaticTuple for ($($tt,)+)
where
$($tt: 'static),+
{
#[inline]
fn distinct() -> bool
{
let ids = [$(std::any::TypeId::of::<$tt>(),)+];
for i in 0 .. ids.len() {
for j in i + 1 .. ids.len() {
if ids[i] == ids[j] {
return false;
}
}
}
true
}
}
impl<$($tt),+> Storable for ($($tt,)+)
where
$($tt: Storable,)+
($($tt,)+): From<($($tt::Generative<'static>,)+)>,
($($tt,)+): BorrowMut<($($tt::Generative<'static>,)+)>,
($($tt,)+): Into<($($tt::Generative<'static>,)+)>
{
type Generative<'c> = ($($tt::Generative<'c>,)+);
}
impl<$($tt),+> TryGenTuple for ($($tt,)+)
where
$($tt: 'static),+
{
type Tuple<'c> = ($(UniqueType<'c, $tt>,)+);
#[inline]
fn try_gen_tuple<'c>(region: Region<'c>) -> Option<Self::Tuple<'c>>
{
<($($tt,)+)>::distinct().then(|| ($(UniqueType(region, PhantomData::<$tt>),)+))
}
}
};
}
gen_tuple!(T0, T1);
gen_tuple!(T0, T1, T2);
gen_tuple!(T0, T1, T2, T3);
gen_tuple!(T0, T1, T2, T3, T4);
gen_tuple!(T0, T1, T2, T3, T4, T5);
gen_tuple!(T0, T1, T2, T3, T4, T5, T6);
gen_tuple!(T0, T1, T2, T3, T4, T5, T6, T7);
gen_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
gen_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);