//! Interface for interfaces.
//!
//! ## Design
//! The design of protocols is based on an idea found in the
//! [`gdbstub`](https://docs.rs/gdbstub/latest/gdbstub/target/ext/index.html) crate.
//! This idea is of so called inlinable dyn extension traits.
//! However, in the form given in `gdbstub` they can't be used for arbitrary interfaces.
//! The main trait still needs to know about all the possible protocols.
//! That is where this module comes in.
//!
//! This module implements a technique we name dynamic inlinable dyn extension traits (DIDETs).
//! DIDETs adds one more layer to IDETs. Instead of a trait that knows all the possible protocols,
//! we have a single trait [`Implementer`] that allows looking up an extension trait
//! using a type ID. This may seem like it defeats the purpose of IDETs, that being to
//! make them inlinable. However, it turns out LLVM (the optimizer) is able to see
//! through this style of runtime reflection. As such, we still gain the benefits of
//! IDETs but with more flexability.
//! Protocols can now be defined in *any* crate and used between arbitrary crates.
//!
//! A protocol is a special trait that can participate as a DIDET. The only thing needed
//! for a protocol is an associated trait object. Because we need to use the
//! [`TypeId`][core::any::TypeId] of a protocol to perform reflection, we can't just use
//! the trait object itself as the protocol type. Instead an uninhabited type is used
//! as a marker for the trait.
//!
//! We then "implement" a protocol for a type by using [`Implementation`]. This provides
//! a mapping from `T` to the protocol's trait object.
//! By itself, [`Implementation`] is not enough for DIDET. A type also needs to implement
//! [`Implementer`] which allows looking up a particular [`Implementation`] trait object
//! from a [`ProtocolId`].
//!
//! The implementation of DIDETs defined by this module allows [`Implementer`] to be object safe.
//! This is done via the help of the [`AnyImpl`] type. This is not required for the core
//! idea of DIDETs.
use crate::any::{IndirectLtAny, LtAny, LtTypeId, Mut, Ref, TypeNameable};
pub trait Implementer<'ctx> {
fn interface(&self, id: LtTypeId<'ctx>) -> Option<IndirectLtAny<'_, 'ctx, Ref>>;
}
pub trait ImplementerMut<'ctx> {
fn interface_mut<'a>(&'a mut self, id: LtTypeId<'ctx>) -> Option<IndirectLtAny<'a, 'ctx, Mut>> where 'ctx: 'a;
}
/// Extension trait for getting the implementation of a protocol.
pub trait ImplementerMutExt<'ctx>: ImplementerMut<'ctx> {
/// Get an implementation given a protocol type.
///
/// This wraps [`Implementer::interface`] and [`AnyImpl::downcast`].
/// If [`Implementer::interface`] returns a [`AnyImpl`] for the wrong protocol then a panic is
/// generated.
fn interface_mut_for<'a, P: TypeNameable<'a, 'ctx>>(&'a mut self) -> Option<&'a mut P>
where
'ctx: 'a;
}
impl<'ctx, T: ImplementerMut<'ctx> + ?Sized> ImplementerMutExt<'ctx> for T {
fn interface_mut_for<'a, P: TypeNameable<'a, 'ctx>>(&'a mut self) -> Option<&'a mut P>
where
'ctx: 'a
{
match self.interface_mut(LtTypeId::of::<P>()) {
Some(interface) => match interface.downcast::<P>() {
Ok(implementation) => Some(implementation),
Err(interface) => panic!(
"unexpected protocol implementation: `{:?}`, expected: `{:?}`",
interface.id(),
LtTypeId::of::<P>()
),
},
None => None,
}
}
}
/// Implement [`Implementer`] and [`Implementation`] for a set of protocols.
#[doc(hidden)]
#[macro_export]
macro_rules! implementer {
{
impl[$a:lifetime, $ctx:lifetime $($generic:tt)*] $name:ty = [$($protocol:ty),* $(,)?];
} => {
impl<$ctx $($generic)*> $crate::protocol::ImplementerMut<$ctx> for $name {
#[inline]
fn interface_mut<$a>(
&$a mut self,
id: $crate::any::LtTypeId<$ctx>
) -> ::core::option::Option<$crate::any::IndirectLtAny<$a, $ctx, $crate::any::Mut>>
where
$ctx: $a
{
match id {
$(id if id == $crate::any::LtTypeId::of::<$protocol>()
=> Some($crate::any::IndirectLtAny::<$a, $ctx, _>::new::<$protocol>(self as _)),)*
_ => None
}
}
}
};
}
#[doc(inline)]
pub use implementer;
#[cfg(test)]
mod test {
use crate::nameable;
use super::*;
#[test]
fn implementer_macro() {
trait Z {}
nameable! {
['a, 'ctx]
dyn Z + 'a where {'ctx: 'a}
}
struct X<T>(T);
impl<T: Clone> Z for X<T> {}
implementer! {
impl['a, 'ctx, T: Clone] X<T> = [
dyn Z + 'a
];
}
}
}