use bytes::{Buf, BufMut};
pub const SUBPROTOCOL_STREAM_WINDOW: u16 = 0x0B00;
pub const STREAM_WINDOW_SIZE: usize = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StreamWindow {
pub stream_id: u64,
pub total_consumed: u64,
}
#[derive(Debug, thiserror::Error)]
pub enum StreamWindowCodecError {
#[error("truncated stream-window message: {0} bytes (need 16)")]
Truncated(usize),
#[error("oversize stream-window message: {0} bytes (need 16)")]
Oversize(usize),
}
impl StreamWindow {
#[inline]
pub fn encode(&self) -> [u8; STREAM_WINDOW_SIZE] {
let mut buf = [0u8; STREAM_WINDOW_SIZE];
(&mut buf[..8]).put_u64_le(self.stream_id);
(&mut buf[8..]).put_u64_le(self.total_consumed);
buf
}
pub fn decode(data: &[u8]) -> Result<Self, StreamWindowCodecError> {
match data.len() {
n if n < STREAM_WINDOW_SIZE => Err(StreamWindowCodecError::Truncated(n)),
n if n > STREAM_WINDOW_SIZE => Err(StreamWindowCodecError::Oversize(n)),
_ => {
let mut cur = std::io::Cursor::new(data);
let stream_id = cur.get_u64_le();
let total_consumed = cur.get_u64_le();
Ok(Self {
stream_id,
total_consumed,
})
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_round_trip() {
let msg = StreamWindow {
stream_id: 0xDEAD_BEEF_CAFE_F00D,
total_consumed: 0x0102_0304_0506_0708,
};
let bytes = msg.encode();
assert_eq!(bytes.len(), STREAM_WINDOW_SIZE);
let parsed = StreamWindow::decode(&bytes).unwrap();
assert_eq!(parsed, msg);
}
#[test]
fn test_decode_truncated_rejected() {
let err = StreamWindow::decode(&[0u8; 15]).unwrap_err();
assert!(matches!(err, StreamWindowCodecError::Truncated(15)));
}
#[test]
fn test_decode_oversize_rejected() {
let err = StreamWindow::decode(&[0u8; 17]).unwrap_err();
assert!(matches!(err, StreamWindowCodecError::Oversize(17)));
}
#[test]
fn test_decode_empty_rejected() {
let err = StreamWindow::decode(&[]).unwrap_err();
assert!(matches!(err, StreamWindowCodecError::Truncated(0)));
}
#[test]
fn test_endianness_is_little_endian() {
let msg = StreamWindow {
stream_id: 1,
total_consumed: 1,
};
let bytes = msg.encode();
assert_eq!(bytes[0], 0x01);
assert_eq!(bytes[1], 0x00);
assert_eq!(bytes[8], 0x01);
assert_eq!(bytes[9], 0x00);
}
}