use std::{any::TypeId, marker::PhantomData, ptr::NonNull};
use atomic_refcell::AtomicRefCell;
use crate::{borrow::ContextBorrow, Error, IntoAccess, Result};
pub struct Context<'a> {
data: &'a dyn Data,
}
unsafe impl Send for Context<'_> {}
unsafe impl Sync for Context<'_> {}
impl<'a> Context<'a> {
pub fn new(data: &'a dyn Data) -> Context {
Self { data }
}
pub fn borrow<T>(&'a self) -> Result<T::Target>
where
T: ContextBorrow<'a>,
{
T::borrow(self)
}
pub fn cell<T: IntoAccess>(&'a self) -> Result<&AtomicRefCell<NonNull<u8>>> {
let access = T::access();
self.data
.get(access.id())
.ok_or_else(|| Error::MissingData(access.name()))
}
}
pub unsafe trait Data {
fn get<'a>(&'a self, ty: TypeId) -> Option<&AtomicRefCell<NonNull<u8>>>;
}
unsafe impl Data for () {
fn get<'a>(&'a self, _: TypeId) -> Option<&AtomicRefCell<NonNull<u8>>> {
None
}
}
unsafe impl<A: 'static + Send + Sync> Data for (AtomicRefCell<NonNull<u8>>, PhantomData<A>) {
fn get<'a>(&'a self, ty: TypeId) -> Option<&AtomicRefCell<NonNull<u8>>> {
if ty == TypeId::of::<A>() {
Some(&self.0)
} else {
None
}
}
}
pub trait IntoData: Send + Sync {
type Target: Data;
unsafe fn into_data(self) -> Self::Target;
}
impl IntoData for () {
type Target = ();
unsafe fn into_data(self) -> Self::Target {
()
}
}
macro_rules! tuple_impls {
() => {};
(($idx:tt => $typ:ident), $( ($nidx:tt => $ntyp:ident), )*) => {
tuple_impls!([($idx, $typ);] $( ($nidx => $ntyp), )*);
tuple_impls!($( ($nidx => $ntyp), )*); };
([$(($accIdx: tt, $accTyp: ident);)+] ($idx:tt => $typ:ident), $( ($nidx:tt => $ntyp:ident), )*) => {
tuple_impls!([($idx, $typ); $(($accIdx, $accTyp); )*] $( ($nidx => $ntyp), ) *);
};
([($idx:tt, $typ:ident); $( ($nidx:tt, $ntyp:ident); )*]) => {
impl<$typ, $( $ntyp ), *> IntoData for (&mut $typ, $(&mut $ntyp,) *)
where
$typ: 'static + Send + Sync,
$($ntyp: 'static + Send + Sync), *
{
type Target = ((AtomicRefCell<NonNull<u8>>, PhantomData<$typ>), $( (AtomicRefCell<NonNull<u8>>, PhantomData<$ntyp>), )*);
unsafe fn into_data(self) -> Self::Target {
(
(AtomicRefCell::new(NonNull::new_unchecked(self.$idx as *mut _ as *mut u8)), PhantomData),
$( (AtomicRefCell::new(NonNull::new_unchecked(self.$nidx as *mut _ as *mut u8)), PhantomData), ) *
)
}
}
unsafe impl<$typ, $( $ntyp ), *> Data for ((AtomicRefCell<NonNull<u8>>, PhantomData<$typ>), $( (AtomicRefCell<NonNull<u8>>, PhantomData<$ntyp>), )*)
where
$typ: 'static,
$($ntyp: 'static), *
{
fn get<'a>(&'a self, ty: TypeId) -> Option<&AtomicRefCell<NonNull<u8>>> {
if ty == TypeId::of::<$typ>() {
Some(&self.$idx.0)
} $(else if ty == TypeId::of::<$ntyp>() {
Some(&self.$nidx.0)
}) *
else {
None
}
}
}
};
}
tuple_impls!(
(9 => J),
(8 => I),
(7 => H),
(6 => G),
(5 => F),
(4 => E),
(3 => D),
(2 => C),
(1 => B),
(0 => A),
);
#[cfg(test)]
mod tests {
use super::{Context, IntoData};
#[test]
fn context() {
let mut a = 64_i32;
let mut b = "Hello, World";
let data = unsafe { (&mut a, &mut b).into_data() };
let context = Context::new(&data);
{
let a = context.borrow::<&i32>().unwrap();
let mut b = context.borrow::<&mut &str>().unwrap();
assert_eq!(*b, "Hello, World");
*b = "Foo Fighters";
drop(b);
let b = context.borrow::<&&str>().unwrap();
assert_eq!(*a, 64);
assert_eq!(*b, "Foo Fighters");
let c = context.borrow::<&f32>();
assert!(c.is_err());
}
}
}