irys 0.0.1

Compile-time trait reflection for Rust
Documentation
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)
    }};
}