use bytes::{Buf, BufMut, Bytes};
use crate::coding::{Decode, DecodeError, Encode, EncodeError};
use super::Version;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Datagram {
pub subscribe: u64,
pub sequence: u64,
pub timestamp: u64,
pub payload: Bytes,
}
impl Encode<Version> for Datagram {
fn encode<W: BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
if !version.has_datagrams() {
return Err(EncodeError::Version);
}
self.subscribe.encode(w, version)?;
self.sequence.encode(w, version)?;
self.timestamp.encode(w, version)?;
if w.remaining_mut() < self.payload.len() {
return Err(EncodeError::Short);
}
w.put_slice(&self.payload);
Ok(())
}
}
impl Decode<Version> for Datagram {
fn decode<R: Buf>(r: &mut R, version: Version) -> Result<Self, DecodeError> {
if !version.has_datagrams() {
return Err(DecodeError::Version);
}
let subscribe = u64::decode(r, version)?;
let sequence = u64::decode(r, version)?;
let timestamp = u64::decode(r, version)?;
let payload = r.copy_to_bytes(r.remaining());
Ok(Self {
subscribe,
sequence,
timestamp,
payload,
})
}
}
#[cfg(test)]
mod test {
use super::*;
use bytes::BytesMut;
#[test]
fn roundtrip() {
let original = Datagram {
subscribe: 7,
sequence: 42,
timestamp: 1_000,
payload: Bytes::from_static(b"hello"),
};
let mut buf = BytesMut::new();
original.encode(&mut buf, Version::Lite05).unwrap();
let mut slice = &buf[..];
let decoded = Datagram::decode(&mut slice, Version::Lite05).unwrap();
assert_eq!(decoded, original);
assert!(!slice.has_remaining(), "payload has no trailing length prefix");
}
#[test]
fn empty_payload() {
let original = Datagram {
subscribe: 0,
sequence: 0,
timestamp: 0,
payload: Bytes::new(),
};
let mut buf = BytesMut::new();
original.encode(&mut buf, Version::Lite05).unwrap();
let mut slice = &buf[..];
let decoded = Datagram::decode(&mut slice, Version::Lite05).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn no_inner_length_prefix() {
let dg = Datagram {
subscribe: 1,
sequence: 2,
timestamp: 3,
payload: Bytes::from_static(b"world"),
};
let buf = dg.encode_bytes(Version::Lite05).unwrap();
assert_eq!(buf.len(), 8);
assert_eq!(&buf[3..], b"world");
}
#[test]
fn rejects_old_versions() {
let dg = Datagram {
subscribe: 1,
sequence: 2,
timestamp: 3,
payload: Bytes::from_static(b"x"),
};
let mut buf = BytesMut::new();
assert!(matches!(
dg.encode(&mut buf, Version::Lite04),
Err(EncodeError::Version)
));
let mut slice = &b"\x01\x02\x03x"[..];
assert!(matches!(
Datagram::decode(&mut slice, Version::Lite04),
Err(DecodeError::Version)
));
}
}