1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
mod id;

pub use any_implementation::AnyImpl;
pub use id::ProtocolId;

pub trait Protocol: 'static {
    type Object<'a, 'ctx: 'a>;
}

pub trait ProtocolExt: Protocol {
    fn id() -> ProtocolId;
}

impl<T: Protocol> ProtocolExt for T {
    fn id() -> ProtocolId {
        ProtocolId::of::<T>()
    }
}

pub trait Implementer<'ctx> {
    fn interface(&mut self, id: ProtocolId) -> Option<AnyImpl<'_, 'ctx>>;
}

pub trait Implementation<'ctx, P: Protocol> {
    fn as_object(&mut self) -> P::Object<'_, 'ctx>;
}

pub trait ImplementerExt<'ctx>: Implementer<'ctx> {
    fn interface_for<P: Protocol>(&mut self) -> Option<&mut dyn Implementation<'ctx, P>>;
}

impl<'ctx, T: Implementer<'ctx> + ?Sized> ImplementerExt<'ctx> for T {
    fn interface_for<P: Protocol>(&mut self) -> Option<&mut dyn Implementation<'ctx, P>> {
        match self.interface(P::id()) {
            Some(interface) => match interface.downcast::<P>() {
                Ok(implementation) => Some(implementation),
                Err(interface) => panic!(
                    "unexpected type ID for protocol implementation: `{:?}`, expected: `{:?}`",
                    interface.id(),
                    P::id()
                ),
            },
            None => None,
        }
    }
}

#[doc(hidden)]
#[macro_export]
macro_rules! implementer {
    {
        impl[$ctx:lifetime $($generic:tt)*] $name:ty = [$($protocol:ty),* $(,)?];
    } => {
        impl<$ctx $($generic)*> $crate::protocol::Implementer<$ctx> for $name {
            #[inline]
            fn interface(&mut self, id: $crate::protocol::ProtocolId) -> ::core::option::Option<$crate::protocol::AnyImpl<'_, $ctx>> {
                match id {
                    $(id if id == $crate::protocol::ProtocolId::of::<$protocol>() => Some($crate::protocol::AnyImpl::new::<$protocol>(self)),)*
                    _ => None
                }
            }
        }

        $crate::implementer! {
            $ctx $name [$($protocol),*] [$($generic)*]
        }
    };
    {
        $ctx:lifetime $name:ty [$protocol:ty] [$($generic:tt)*]
    } => {
        impl<$ctx $($generic)*> $crate::protocol::Implementation<$ctx, $protocol> for $name {
            fn as_object(&mut self) -> <$protocol as $crate::protocol::Protocol>::Object<'_, $ctx> {
                self
            }
        }
    };
    {
        $ctx:lifetime $name:ty [$($protocol:ty),*] $generic:tt
    } => {
        $($crate::implementer! {
            $ctx $name [$protocol] $generic
        })*
    };
}
#[doc(inline)]
pub use implementer;

mod any_implementation {
    use core::{marker::PhantomData, mem::MaybeUninit};

    use super::{Implementation, Protocol, ProtocolExt, ProtocolId};

    trait ErasedImplementation<'ctx> {}

    const DYN_PTR_SIZE: usize = core::mem::size_of::<&mut dyn ErasedImplementation<'static>>();

    pub struct AnyImpl<'a, 'ctx> {
        id: ProtocolId,
        fat_ptr: MaybeUninit<[u8; DYN_PTR_SIZE]>,
        _marker: PhantomData<&'a mut dyn ErasedImplementation<'ctx>>,
    }

    impl<'a, 'ctx> AnyImpl<'a, 'ctx> {
        pub fn new<P: Protocol>(implementation: &'a mut dyn Implementation<'ctx, P>) -> Self {
            Self {
                id: P::id(),
                // SAFETY: A maybe uninit array of bytes can hold any pointer.
                // Additionally, transmute makes sure the size is correct.
                fat_ptr: unsafe { core::mem::transmute(implementation) },
                _marker: PhantomData,
            }
        }

        pub fn downcast<P: Protocol>(self) -> Result<&'a mut dyn Implementation<'ctx, P>, Self> {
            if self.id == P::id() {
                // SAFETY: Only `new` can make a value of this type, and it stores the ID of `P`.
                // If the IDs are equal then we can act like any and downcast back to the real
                // type.
                //
                // An important note is this method takes ownership. Which allows it to return
                // the borrow with the `'walking` lifetime instead of a sub-borrow.
                Ok(unsafe { core::mem::transmute(self.fat_ptr) })
            } else {
                Err(self)
            }
        }

        pub fn id(&self) -> ProtocolId {
            self.id
        }
    }
}