-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrepr.rs
152 lines (145 loc) · 4.74 KB
/
repr.rs
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use std::any::Any;
/// We can use a dynamic boxed message to pass messages, and represent all messages in a protocol as individual structs.
/// However if we want to send them over a network connection we will need to serialise to some wire format, and at that
/// point we have to use tagging, so we can recognise what type to deserialise into.
///
/// It helps then to use something like an enum to represent the data, but the way protocols work is to use the sent and
/// received types in their generic signatures, where enums would not work.
///
/// For this reason the channels have a generic parameter for the representation of the messages, which can be an outer
/// enum that handles all supported protocols, paired with `From` and `TryInto` traits for each possible message we want
/// to send during the sessions.
///
/// However for in-memory use cases `DynMessage` still works fine.
pub type DynMessage = Box<dyn Any + Send + Sync + 'static>;
/// Define for the wire representation, so that the raw messages can be lifted into it,
/// and later the representation can be cast back into the expected types.
///
/// Similar to `From<T> for R` plus `TryInto<T> for R`.
/// Unfortunately the built-in implementations lead to conflicts for `DynMessage`.
///
/// ```
/// use async_session_types::*;
///
/// let repr: DynMessage = Repr::from(123u8);
/// let msg: u8 = Repr::try_into(repr).unwrap();
/// ```
pub trait Repr<T>: Send + Sync + 'static
where
Self: Sized,
{
/// Convert a raw type to the common representation.
fn from(v: T) -> Self;
/// Try to convert the representation back into one of the raw message types.
fn try_into(self) -> Result<T, Self>;
/// Check whether the representation can be turned into this raw type, without consuming.
fn can_into(&self) -> bool;
}
/// We can turn anything into a `DynMessage`.
impl<T: 'static + Send + Sync> Repr<T> for DynMessage {
fn from(v: T) -> Self {
Box::new(v)
}
fn try_into(self) -> Result<T, Self> {
self.downcast::<T>().map(|b| *b)
}
fn can_into(&self) -> bool {
self.is::<T>()
}
}
/// The `repr_impl` macro creates `Repr` implementations for a type used on the wire,
/// for each protocol message type enumerated in the call.
///
/// The macro call consists of `<wrapper-type-name>`, followed by lines of:
/// `<raw-type-name>: ( <fn-raw-to-repr> , <pattern-match-repr> => <captured-raw> )`.
///
/// # Example
/// ```
/// use async_session_types::*;
///
/// struct Foo(pub u8);
/// struct Bar(pub String);
/// enum Message {
/// Foo(Foo),
/// Bar(Bar),
/// }
///
/// repr_impl! {
/// Message {
/// Foo: ( Message::Foo, Message::Foo(x) => x ),
/// Bar: ( |x| Message::Bar(x), Message::Bar(x) => x )
/// }
/// }
/// ```
///
/// Note that there's no comma after the last item!
#[macro_export]
macro_rules! repr_impl {
($repr:ty { $($msg:ty : ( $lift:expr, $pattern:pat => $x:expr ) ),+ }) => {
$(
impl Repr<$msg> for $repr {
fn from(v: $msg) -> Self {
$lift(v)
}
fn try_into(self) -> Result<$msg, Self> {
match self {
$pattern => Ok($x),
other => Err(other)
}
}
fn can_into(&self) -> bool {
// Using the same pattern that normally extracts the raw type
// only for checking if it succeeds, without using the variables.
#[allow(unused_variables)]
match &self {
$pattern => true,
_ => false
}
}
}
)*
};
}
/// The `repr_bound` macro creates a type alias that can be used as bound for the generic
/// wire type, instead of listing all messages used by the protocol.
///
/// # Example
/// ```
/// #![feature(trait_alias)]
///
/// use async_session_types::*;
///
/// struct Foo(pub u8);
/// struct Bar(pub String);
///
/// repr_bound! { FooBarReprs [Foo, Bar] }
///
/// type Server = Recv<Foo, Send<Bar, Eps>>;
///
/// fn server<R: FooBarReprs>(c: Chan<Server, (), R>) -> SessionResult<()> {
/// unimplemented!()
/// }
/// ```
///
/// Note that there's no comma after the last item!
///
/// Requires `#![feature(trait_alias)]`.
#[macro_export]
macro_rules! repr_bound {
// https://stackoverflow.com/a/61189128
(
// e.g. `pub MyReprs<T: Foo + Bar> [ Spam<T>, Eggs ]`
$vis:vis $repr_traits:ident
$(<
$($gen:tt $(: $b:tt $(+ $bs:tt)* )? ),+
>)?
[
$($msg:ty),+
]
) => {
$vis trait $repr_traits
$(<
$($gen $(: $b $(+ $bs)* )? ),+
>)? = 'static $( + Repr<$msg> )+;
};
}