async_session_types/
repr.rs

1use std::any::Any;
2
3/// We can use a dynamic boxed message to pass messages, and represent all messages in a protocol as individual structs.
4/// However if we want to send them over a network connection we will need to serialise to some wire format, and at that
5/// point we have to use tagging, so we can recognise what type to deserialise into.
6///
7/// It helps then to use something like an enum to represent the data, but the way protocols work is to use the sent and
8/// received types in their generic signatures, where enums would not work.
9///
10/// For this reason the channels have a generic parameter for the representation of the messages, which can be an outer
11/// enum that handles all supported protocols, paired with `From` and `TryInto` traits for each possible message we want
12/// to send during the sessions.
13///
14/// However for in-memory use cases `DynMessage` still works fine.
15pub type DynMessage = Box<dyn Any + Send + Sync + 'static>;
16
17/// Define for the wire representation, so that the raw messages can be lifted into it,
18/// and later the representation can be cast back into the expected types.
19///
20/// Similar to `From<T> for R` plus `TryInto<T> for R`.
21/// Unfortunately the built-in implementations lead to conflicts for `DynMessage`.
22///
23/// ```
24/// use async_session_types::*;
25///
26/// let repr: DynMessage = Repr::from(123u8);
27/// let msg: u8 = Repr::try_into(repr).unwrap();
28/// ```
29pub trait Repr<T>: Send + Sync + 'static
30where
31    Self: Sized,
32{
33    /// Convert a raw type to the common representation.
34    fn from(v: T) -> Self;
35    /// Try to convert the representation back into one of the raw message types.
36    fn try_into(self) -> Result<T, Self>;
37    /// Check whether the representation can be turned into this raw type, without consuming.
38    fn can_into(&self) -> bool;
39}
40
41/// We can turn anything into a `DynMessage`.
42impl<T: 'static + Send + Sync> Repr<T> for DynMessage {
43    fn from(v: T) -> Self {
44        Box::new(v)
45    }
46    fn try_into(self) -> Result<T, Self> {
47        self.downcast::<T>().map(|b| *b)
48    }
49    fn can_into(&self) -> bool {
50        self.is::<T>()
51    }
52}
53
54/// The `repr_impl` macro creates `Repr` implementations for a type used on the wire,
55/// for each protocol message type enumerated in the call.
56///
57/// The macro call consists of `<wrapper-type-name>`, followed by lines of:
58/// `<raw-type-name>: ( <fn-raw-to-repr> , <pattern-match-repr> => <captured-raw> )`.
59///
60/// # Example
61/// ```
62/// use async_session_types::*;
63///
64/// struct Foo(pub u8);
65/// struct Bar(pub String);
66/// enum Message {
67///   Foo(Foo),
68///   Bar(Bar),
69/// }
70///
71/// repr_impl! {
72///   Message {
73///     Foo: ( Message::Foo, Message::Foo(x) => x ),
74///     Bar: ( |x| Message::Bar(x), Message::Bar(x) => x )
75///   }
76/// }
77/// ```
78///
79/// Note that there's no comma after the last item!
80#[macro_export]
81macro_rules! repr_impl {
82    ($repr:ty { $($msg:ty : ( $lift:expr, $pattern:pat => $x:expr ) ),+ }) => {
83        $(
84            impl Repr<$msg> for $repr {
85                fn from(v: $msg) -> Self {
86                    $lift(v)
87                }
88
89                fn try_into(self) -> Result<$msg, Self> {
90                    match self {
91                        $pattern => Ok($x),
92                        other => Err(other)
93                    }
94                }
95
96                fn can_into(&self) -> bool {
97                    // Using the same pattern that normally extracts the raw type
98                    // only for checking if it succeeds, without using the variables.
99                    #[allow(unused_variables)]
100                    match &self {
101                        $pattern => true,
102                        _ => false
103                    }
104                }
105            }
106        )*
107    };
108}
109
110/// The `repr_bound` macro creates a type alias that can be used as bound for the generic
111/// wire type, instead of listing all messages used by the protocol.
112///
113/// # Example
114/// ```
115/// #![feature(trait_alias)]
116///
117/// use async_session_types::*;
118///
119/// struct Foo(pub u8);
120/// struct Bar(pub String);
121///
122/// repr_bound! { FooBarReprs [Foo, Bar] }
123///
124/// type Server = Recv<Foo, Send<Bar, Eps>>;
125///
126/// fn server<R: FooBarReprs>(c: Chan<Server, (), R>) -> SessionResult<()> {
127///     unimplemented!()
128/// }
129/// ```
130///
131/// Note that there's no comma after the last item!
132///
133/// Requires `#![feature(trait_alias)]`.
134#[macro_export]
135macro_rules! repr_bound {
136    // https://stackoverflow.com/a/61189128
137    (
138        // e.g. `pub MyReprs<T: Foo + Bar> [ Spam<T>, Eggs ]`
139        $vis:vis $repr_traits:ident
140        $(<
141            $($gen:tt $(: $b:tt $(+ $bs:tt)* )? ),+
142        >)?
143        [
144            $($msg:ty),+
145        ]
146    ) => {
147        $vis trait $repr_traits
148        $(<
149            $($gen $(: $b $(+ $bs)* )? ),+
150        >)? = 'static $( + Repr<$msg> )+;
151    };
152}