aversion/group.rs
1//! Define message groups for automatic dispatching.
2//!
3//! A message group is a collection of messages that may be used together.
4//! For example, a file format or a network protocol may form a group.
5//!
6
7use crate::{MessageId, Versioned};
8use serde::de::DeserializeOwned;
9use serde::Serialize;
10use std::any::type_name;
11
12/// A data structure that contains a message-id and version fields.
13
14pub trait GroupHeader {
15 /// Retrieve the message id.
16 fn msg_id(&self) -> u16;
17 /// Retrieve the message version.
18 fn msg_ver(&self) -> u16;
19}
20
21/// A trait for deserializing any version of a [`Versioned`] data structure.
22///
23/// This trait will normally be derived using `#[derive(Versioned)]`.
24///
25// How will the macro know which versions exist?
26// a) Macro will assume that every version [1..latest] exists
27// - and maybe there's a macro to generate stubs for missing versions?
28// b) User needs to specify a range or list of versions
29pub trait UpgradeLatest: DeserializeOwned + Versioned {
30 /// Deserialize version `ver` of the target struct, then upgrade it to the latest version.
31 fn upgrade_latest<Src>(src: &mut Src, header: Src::Header) -> Result<Self, Src::Error>
32 where
33 Src: DataSource;
34}
35
36/// `DataSource` allows user-defined IO, deserialization, and
37/// error handling.
38///
39pub trait DataSource {
40 /// A user-defined error type.
41 ///
42 /// This error type will be returned from [`read_header`][Self::read_header]
43 /// and [`read_message`][Self::read_message].
44 /// It's probably a good idea for it to be able to represent IO errors,
45 /// deserialization errors, and "unknown message" errors.
46 type Error;
47 /// A user-defined header struct.
48 ///
49 /// The `Header` is a way of communicating what kind of message is being
50 /// sent, along with the message version.
51 type Header: GroupHeader;
52
53 /// Read a header from the data source.
54 ///
55 /// This is a user-defined function that will read the next header.
56 /// The data in the header will be used to determine what kind of
57 /// message comes next.
58 ///
59 fn read_header(&mut self) -> Result<Self::Header, Self::Error>;
60
61 /// Read a message from the data source.
62 ///
63 /// This is a user-defined function that will deserialize a message
64 /// of type `T`.
65 fn read_message<T>(&mut self, header: &Self::Header) -> Result<T, Self::Error>
66 where
67 T: DeserializeOwned;
68
69 /// An unknown message id was received.
70 ///
71 /// This is a user-defined function that constructs an error value.
72 /// This function will be called by [`GroupDeserialize::read_message`]
73 /// when an unknown message is received (a message with an unknown
74 /// message id).
75 ///
76 fn unknown_message(&self, msg_id: u16) -> Self::Error {
77 panic!("unknown message id {}", msg_id);
78 }
79
80 /// An unknown version of a known message was received.
81 ///
82 /// This is a user-defined function that constructs an error value.
83 /// This function will be called by [`GroupDeserialize::read_message`]
84 /// when a known message id is received, but with a message version that
85 /// is unknown.
86 ///
87 fn unknown_version<T>(&self, ver: u16) -> Self::Error {
88 panic!("unknown version {} for {}", ver, type_name::<T>());
89 }
90
91 /// Expected a specific message type, but got a different message id.
92 ///
93 /// This is a user-defined function that constructs an error value.
94 /// This function will be called by [`GroupDeserialize::expect_message`]
95 /// when a different message id is received from the message that was
96 /// specified.
97 ///
98 fn unexpected_message<T>(&self, msg_id: u16) -> Self::Error {
99 panic!(
100 "unexpected message id {} (expected {})",
101 msg_id,
102 type_name::<T>()
103 );
104 }
105}
106
107/// Useful functions for `DataSource`.
108///
109/// There is a blanket implementation of this trait, so that any
110/// [`DataSource`] type can use these functions.
111pub trait DataSourceExt: DataSource {
112 /// Read a specific message type from the `DataSource`.
113 ///
114 /// This will read the message header, and if the message id matches
115 /// the type `T` that was requested, read the message.
116 /// The message will be upgraded to the latest version, and then
117 /// returned.
118 fn expect_message<T>(&mut self) -> Result<T, Self::Error>
119 where
120 T: MessageId + UpgradeLatest;
121}
122
123impl<Src> DataSourceExt for Src
124where
125 Src: DataSource,
126{
127 fn expect_message<T>(&mut self) -> Result<T, Src::Error>
128 where
129 Src: DataSource,
130 T: MessageId + UpgradeLatest,
131 {
132 let header: Src::Header = self.read_header()?;
133 if header.msg_id() == T::MSG_ID {
134 T::upgrade_latest(self, header)
135 } else {
136 // Call the user-supplied error fn
137 Err(self.unexpected_message::<T>(header.msg_id()))
138 }
139 }
140}
141
142/// A derived trait that can deserialize any message from a group.
143pub trait GroupDeserialize: Sized {
144 /// Read the next message from the `DataSource`.
145 ///
146 /// This will read the message header, and if the message id and
147 /// message version are known, also read the message.
148 /// The message will be upgraded to the latest version, and then
149 /// returned as an enum variant (in the `Self` enum).
150 fn read_message<Src>(src: &mut Src) -> Result<Self, Src::Error>
151 where
152 Src: DataSource;
153}
154
155/// `DataSink` allows user-defined IO, deserialization, and
156/// error handling.
157///
158pub trait DataSink {
159 /// A user-defined error type.
160 ///
161 /// This error type will be returned from all trait member functions.
162 /// It's probably a good idea for it to be able to represent IO errors,
163 /// deserialization errors, and "unknown message" errors.
164 type Error;
165
166 /// Write a header and message to the data sink.
167 ///
168 fn write_message<T>(&mut self, msg: &T) -> Result<(), Self::Error>
169 where
170 T: Serialize + Versioned,
171 T::Base: MessageId;
172}