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 /// Did not provide the complete byte-string necessary to deserialize the [`ConnectDatagram`].
17 BytesParseFail(TryFromSliceError),
20 impl Error for DatagramError {}
22 impl std::fmt::Display for DatagramError {
23 fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
25 DatagramError::EmptyBody => formatter.write_str("tried to construct a `ConnectDatagram` with an empty message body"),
26 DatagramError::IncompleteBytes => formatter.write_str("did not provide the complete byte-string necessary to deserialize the `ConnectDatagram`"),
27 DatagramError::BytesParseFail(err) => std::fmt::Display::fmt(err, formatter),
32 /// A simple size-prefixed packet format containing a version tag, recipient tag, and message body.
35 pub struct ConnectDatagram {
38 data: Option<Vec<u8>>,
41 impl ConnectDatagram {
42 /// Creates a new [`ConnectDatagram`] based on an intended recipient and message body.
44 /// This will return a [EmptyBody](`DatagramError::EmptyBody`) error if the `data` parameter
45 /// contains no bytes, or in other words, when there is no message body.
47 /// The version field is decided by the library version and used to maintain backwards
48 /// compatibility with previous datagram formats.
50 pub fn new(recipient: u16, data: Vec<u8>) -> Result<Self, DatagramError> {
58 Err(DatagramError::EmptyBody)
62 /// Gets the version number of the datagram.
64 pub fn version(&self) -> u16 {
68 /// Gets the recipient of the datagram.
70 pub fn recipient(&self) -> u16 {
74 /// Gets the message body of the datagram.
76 pub fn data(&self) -> Option<&Vec<u8>> {
80 /// Takes ownership of the message body of the datagram.
82 pub fn take_data(&mut self) -> Option<Vec<u8>> {
86 /// Calculates the size-prefixed serialized byte-size of the datagram.
88 /// This will include the byte-size of the size-prefix.
90 pub fn size(&self) -> usize {
91 let data_len = if let Some(data) = self.data() {
100 /// Constructs a serialized representation of the datagram contents.
102 pub(crate) fn bytes(&self) -> Vec<u8> {
103 let mut bytes = Vec::with_capacity(self.size());
105 bytes.extend(&self.version.to_be_bytes());
106 bytes.extend(&self.recipient.to_be_bytes());
108 if let Some(data) = self.data() {
109 bytes.extend(data.as_slice());
115 /// Serializes the datagram.
117 pub fn encode(self) -> Vec<u8> {
118 let content_encoded = self.bytes();
119 let size: u32 = (content_encoded.len()) as u32;
121 let mut bytes = Vec::from(size.to_be_bytes());
122 bytes.extend(content_encoded);
127 /// Deserializes the datagram from a buffer.
129 /// The buffer **should not** contain the size-prefix, and only contain the byte contents of the
130 /// struct (version, recipient, and message body).
132 pub fn decode(mut buffer: Vec<u8>) -> Result<Self, DatagramError> {
133 if buffer.len() > 4 {
134 let mem_size = std::mem::size_of::<u16>();
135 let data = buffer.split_off(mem_size * 2);
137 let (version_bytes, recipient_bytes) = buffer.split_at(mem_size);
139 match version_bytes.try_into() {
140 Ok(version_slice) => match recipient_bytes.try_into() {
141 Ok(recipient_slice) => {
142 let version = u16::from_be_bytes(version_slice);
143 let recipient = u16::from_be_bytes(recipient_slice);
152 Err(err) => Err(DatagramError::BytesParseFail(err)),
155 Err(err) => Err(DatagramError::BytesParseFail(err)),
158 Err(DatagramError::IncompleteBytes)
165 use crate::protocol::ConnectDatagram;
168 fn serialized_size() -> anyhow::Result<()> {
169 let mut data = Vec::new();
173 assert_eq!(5, data.len());
175 let sample = ConnectDatagram::new(1, data)?;
176 assert_eq!(8 + 5, sample.encode().len());
182 fn take_data() -> anyhow::Result<()> {
183 let mut data = Vec::new();
188 let mut sample = ConnectDatagram::new(1, data)?;
190 let taken_data = sample.take_data().unwrap();
191 assert!(sample.data().is_none());
192 assert_eq!(5, taken_data.len());
198 async fn encode_and_decode() -> anyhow::Result<()> {
199 let mut data = Vec::new();
203 assert_eq!(5, data.len());
205 let sample = ConnectDatagram::new(1, data)?;
206 let serialized_size = sample.size();
207 assert_eq!(8 + 5, serialized_size);
209 let mut payload = sample.encode();
210 assert_eq!(serialized_size, payload.len());
212 let payload = payload.split_off(std::mem::size_of::<u32>());
213 let sample_back_res = ConnectDatagram::decode(payload);
214 assert!(sample_back_res.is_ok());
216 let sample_back = sample_back_res.unwrap();
217 assert_eq!(sample_back.version(), 1);
218 assert_eq!(sample_back.recipient(), 1);
219 assert_eq!(sample_back.data().unwrap().len(), 5);