1 use std::array::TryFromSliceError;
2 use std::convert::TryInto;
5 const VERSION: u16 = 1;
7 /// Encountered when there is an issue constructing, serializing, or deserializing a [`ConnectDatagram`].
9 #[derive(Debug, Clone)]
10 pub enum DatagramError {
11 /// Tried to construct a [`ConnectDatagram`] with an empty message body.
14 /// Tried to construct a [`ConnectDatagram`] with a message body larger than 100MB.
17 /// Did not provide the complete byte-string necessary to deserialize the [`ConnectDatagram`].
20 /// Wraps a [`TryFromSliceError`] encountered when the version or recipient tags cannot be
21 /// parsed from the provided bytes.
22 BytesParseFail(TryFromSliceError),
25 impl Error for DatagramError {}
27 impl std::fmt::Display for DatagramError {
28 fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
30 DatagramError::EmptyMessage => formatter.write_str("tried to construct a `ConnectDatagram` with an empty message body"),
31 DatagramError::TooLargeMessage => formatter.write_str("tried to construct a `ConnectDatagram` with a message body larger than 100MB"),
32 DatagramError::IncompleteBytes => formatter.write_str("did not provide the complete byte-string necessary to deserialize the `ConnectDatagram`"),
33 DatagramError::BytesParseFail(err) => std::fmt::Display::fmt(err, formatter),
38 /// A simple size-prefixed packet format containing a version tag, recipient tag, and message body.
40 /// The version tag is decided by the library version and used to maintain backwards
41 /// compatibility with previous datagram formats.
44 pub struct ConnectDatagram {
47 data: Option<Vec<u8>>,
50 impl ConnectDatagram {
51 /// Creates a new [`ConnectDatagram`] based on an intended recipient and message body.
53 /// The version tag is decided by the library version and used to maintain backwards
54 /// compatibility with previous datagram formats.
56 /// This will return a [EmptyMessage](`DatagramError::EmptyMessage`) error if the `data`
57 /// parameter contains no bytes, or in other words, when there is no message body.
59 /// This will return a [TooLargeMessage](`DatagramError::TooLargeMessage`) error if the `data`
60 /// parameter contains a buffer size greater than 100,000,000 (bytes), or 100MB.
62 pub fn new(recipient: u16, data: Vec<u8>) -> Result<Self, DatagramError> {
63 if data.len() > 100_000_000 {
64 Err(DatagramError::TooLargeMessage)
65 } else if data.len() > 0 {
72 Err(DatagramError::EmptyMessage)
76 /// Gets the version number of the datagram.
78 pub fn version(&self) -> u16 {
82 /// Gets the recipient of the datagram.
84 pub fn recipient(&self) -> u16 {
88 /// Gets the message body of the datagram.
90 pub fn data(&self) -> Option<&Vec<u8>> {
94 /// Takes ownership of the message body of the datagram.
96 pub fn take_data(&mut self) -> Option<Vec<u8>> {
100 /// Calculates the size-prefixed serialized byte-size of the datagram.
102 /// This will include the byte-size of the size-prefix.
104 pub fn size(&self) -> usize {
105 let data_len = if let Some(data) = self.data() {
114 /// Constructs a serialized representation of the datagram contents.
116 pub(crate) fn bytes(&self) -> Vec<u8> {
117 let mut bytes = Vec::with_capacity(self.size());
119 bytes.extend(&self.version.to_be_bytes());
120 bytes.extend(&self.recipient.to_be_bytes());
122 if let Some(data) = self.data() {
123 bytes.extend(data.as_slice());
129 /// Serializes the datagram.
131 pub fn encode(self) -> Vec<u8> {
132 let content_encoded = self.bytes();
133 let size: u32 = (content_encoded.len()) as u32;
135 let mut bytes = Vec::from(size.to_be_bytes());
136 bytes.extend(content_encoded);
141 /// Deserializes the datagram from a buffer.
143 /// The buffer **should not** contain the size-prefix, and only contain the byte contents of the
144 /// struct (version, recipient, and message body).
146 pub fn decode(mut buffer: Vec<u8>) -> Result<Self, DatagramError> {
147 if buffer.len() > 4 {
148 let mem_size = std::mem::size_of::<u16>();
149 let data = buffer.split_off(mem_size * 2);
151 let (version_bytes, recipient_bytes) = buffer.split_at(mem_size);
153 match version_bytes.try_into() {
154 Ok(version_slice) => match recipient_bytes.try_into() {
155 Ok(recipient_slice) => {
156 let version = u16::from_be_bytes(version_slice);
157 let recipient = u16::from_be_bytes(recipient_slice);
166 Err(err) => Err(DatagramError::BytesParseFail(err)),
169 Err(err) => Err(DatagramError::BytesParseFail(err)),
172 Err(DatagramError::IncompleteBytes)
179 use crate::protocol::ConnectDatagram;
182 fn serialized_size() -> anyhow::Result<()> {
183 let mut data = Vec::new();
187 assert_eq!(5, data.len());
189 let sample = ConnectDatagram::new(1, data)?;
190 assert_eq!(8 + 5, sample.encode().len());
196 fn take_data() -> anyhow::Result<()> {
197 let mut data = Vec::new();
202 let mut sample = ConnectDatagram::new(1, data)?;
204 let taken_data = sample.take_data().unwrap();
205 assert!(sample.data().is_none());
206 assert_eq!(5, taken_data.len());
212 async fn encode_and_decode() -> anyhow::Result<()> {
213 let mut data = Vec::new();
217 assert_eq!(5, data.len());
219 let sample = ConnectDatagram::new(1, data)?;
220 let serialized_size = sample.size();
221 assert_eq!(8 + 5, serialized_size);
223 let mut payload = sample.encode();
224 assert_eq!(serialized_size, payload.len());
226 let payload = payload.split_off(std::mem::size_of::<u32>());
227 let sample_back_res = ConnectDatagram::decode(payload);
228 assert!(sample_back_res.is_ok());
230 let sample_back = sample_back_res.unwrap();
231 assert_eq!(sample_back.version(), 1);
232 assert_eq!(sample_back.recipient(), 1);
233 assert_eq!(sample_back.data().unwrap().len(), 5);