use std::any::Any;
use std::collections::HashMap;
pub use seq_macro::seq;
pub use iris_macros::register_capability;
pub trait Capability: 'static {
type Handle: ?Sized + 'static;
const SLOT: u64;
}
struct CastFn {
cast_ref: Box<dyn Fn(&dyn Any) -> [usize; 2] + Send + Sync>,
cast_mut: Box<dyn Fn(&mut dyn Any) -> [usize; 2] + Send + Sync>,
}
pub struct CapabilityMap {
entries: HashMap<u64, CastFn>,
}
impl CapabilityMap {
pub fn new() -> Self {
Self { entries: HashMap::new() }
}
pub fn insert_cast<C: Capability>(
&mut self,
cast_ref: impl Fn(&dyn Any) -> &C::Handle + Send + Sync + 'static,
cast_mut: impl Fn(&mut dyn Any) -> &mut C::Handle + Send + Sync + 'static,
) {
self.entries.insert(C::SLOT, CastFn {
cast_ref: Box::new(move |any| {
let handle: &C::Handle = cast_ref(any);
unsafe { std::mem::transmute_copy::<&C::Handle, [usize; 2]>(&handle) }
}),
cast_mut: Box::new(move |any| {
let handle: &mut C::Handle = cast_mut(any);
unsafe { std::mem::transmute_copy::<&mut C::Handle, [usize; 2]>(&handle) }
}),
});
}
pub fn has<C: Capability>(&self) -> bool {
self.entries.contains_key(&C::SLOT)
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
pub struct Envelope {
data: Box<dyn Any + Send + Sync>,
caps: CapabilityMap,
}
impl Envelope {
pub fn from_parts(data: impl Any + Send + Sync, caps: CapabilityMap) -> Self {
Self { data: Box::new(data), caps }
}
pub fn get<C: Capability>(&self) -> Option<&C::Handle> {
let cast_fn = self.caps.entries.get(&C::SLOT)?;
let fat = (cast_fn.cast_ref)(&*self.data);
unsafe { Some(&*std::mem::transmute_copy::<[usize; 2], *const C::Handle>(&fat)) }
}
pub fn get_mut<C: Capability>(&mut self) -> Option<&mut C::Handle> {
let cast_fn = self.caps.entries.get(&C::SLOT)?;
let fat = (cast_fn.cast_mut)(&mut *self.data);
unsafe { Some(&mut *std::mem::transmute_copy::<[usize; 2], *mut C::Handle>(&fat)) }
}
pub fn has<C: Capability>(&self) -> bool {
self.caps.has::<C>()
}
pub fn data<T: 'static>(&self) -> Option<&T> {
self.data.downcast_ref()
}
pub fn data_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.data.downcast_mut()
}
pub fn into_data<T: 'static>(self) -> Option<T> {
self.data.downcast().ok().map(|b| *b)
}
pub fn capability_count(&self) -> usize {
self.caps.len()
}
}
pub struct Probe<'a, T, const N: u64>(pub &'a T);
pub trait HasCap<Marker, const N: u64> {
fn probe(&self, caps: &mut CapabilityMap);
}
pub trait NoCap<const N: u64> {
fn probe(&self, _caps: &mut CapabilityMap) {}
}
impl<T, const N: u64> NoCap<N> for &Probe<'_, T, N> {}
#[macro_export]
macro_rules! probe_slot {
($msg:expr, $caps:expr, $n:literal) => {{
use $crate::HasCap as _;
use $crate::NoCap as _;
(&$crate::Probe::<_, $n>($msg)).probe($caps);
}};
}
#[macro_export]
macro_rules! reflect {
($msg:expr) => {{
let msg = $msg;
let mut caps = $crate::CapabilityMap::new();
$crate::seq!(N in 0..256 {
$crate::probe_slot!(&msg, &mut caps, N);
});
$crate::Envelope::from_parts(msg, caps)
}};
}