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}