use crate::codec::{read_bytes, read_u32_le};
use crate::error::WireError;
use bytes::{BufMut, BytesMut};
use std::borrow::Cow;
use std::num::NonZeroU32;
pub const STATUS_OK: u32 = 0;
#[derive(Debug)]
pub struct RequestFrame<'a> {
pub code: u32,
pub payload: &'a [u8],
}
impl<'a> RequestFrame<'a> {
pub const HEADER_SIZE: usize = 8;
pub fn decode(buf: &'a [u8]) -> Result<(Self, usize), WireError> {
let length = read_u32_le(buf, 0)? as usize;
if length < 4 {
return Err(WireError::Validation(Cow::Borrowed(
"request frame length must be at least 4 (code size)",
)));
}
let code = read_u32_le(buf, 4)?;
let payload_len = length - 4;
let payload = read_bytes(buf, Self::HEADER_SIZE, payload_len)?;
let total = Self::HEADER_SIZE + payload_len;
Ok((Self { code, payload }, total))
}
pub fn encode(code: u32, payload: &[u8], out: &mut BytesMut) -> Result<(), WireError> {
let length = payload
.len()
.checked_add(4)
.and_then(|n| u32::try_from(n).ok())
.ok_or(WireError::PayloadTooLarge {
size: payload.len(),
max: u32::MAX as usize - 4,
})?;
out.reserve(Self::HEADER_SIZE + payload.len());
out.put_u32_le(length);
out.put_u32_le(code);
out.put_slice(payload);
Ok(())
}
#[must_use]
pub const fn encoded_size(payload_len: usize) -> Option<usize> {
Self::HEADER_SIZE.checked_add(payload_len)
}
}
#[derive(Debug)]
pub struct ResponseFrame<'a> {
pub status: u32,
pub payload: &'a [u8],
}
impl<'a> ResponseFrame<'a> {
pub const HEADER_SIZE: usize = 8;
pub fn decode(buf: &'a [u8]) -> Result<(Self, usize), WireError> {
let status = read_u32_le(buf, 0)?;
let length = read_u32_le(buf, 4)? as usize;
let payload = read_bytes(buf, Self::HEADER_SIZE, length)?;
let total = Self::HEADER_SIZE + length;
Ok((Self { status, payload }, total))
}
pub fn encode_ok(payload: &[u8], out: &mut BytesMut) -> Result<(), WireError> {
let length = u32::try_from(payload.len()).map_err(|_| WireError::PayloadTooLarge {
size: payload.len(),
max: u32::MAX as usize,
})?;
out.reserve(Self::HEADER_SIZE + payload.len());
out.put_u32_le(STATUS_OK);
out.put_u32_le(length);
out.put_slice(payload);
Ok(())
}
pub fn encode_error(status: NonZeroU32, out: &mut BytesMut) {
out.reserve(Self::HEADER_SIZE);
out.put_u32_le(status.get());
out.put_u32_le(0);
}
#[must_use]
pub const fn is_ok(&self) -> bool {
self.status == STATUS_OK
}
#[must_use]
pub const fn encoded_size(payload_len: usize) -> Option<usize> {
Self::HEADER_SIZE.checked_add(payload_len)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn request_roundtrip() {
let payload = b"hello world";
let mut buf = BytesMut::with_capacity(RequestFrame::encoded_size(payload.len()).unwrap());
RequestFrame::encode(42, payload, &mut buf).unwrap();
let (frame, consumed) = RequestFrame::decode(&buf).unwrap();
assert_eq!(consumed, buf.len());
assert_eq!(frame.code, 42);
assert_eq!(frame.payload, payload);
}
#[test]
fn request_empty_payload() {
let mut buf = BytesMut::with_capacity(RequestFrame::HEADER_SIZE);
RequestFrame::encode(1, &[], &mut buf).unwrap();
let (frame, consumed) = RequestFrame::decode(&buf).unwrap();
assert_eq!(consumed, 8);
assert_eq!(frame.code, 1);
assert!(frame.payload.is_empty());
}
#[test]
fn request_length_field_includes_code() {
let payload = b"test";
let mut buf = BytesMut::new();
RequestFrame::encode(99, payload, &mut buf).unwrap();
let length = u32::from_le_bytes(buf[0..4].try_into().unwrap());
assert_eq!(length, 4 + 4); }
#[test]
fn request_truncated_header() {
let buf = [0u8; 7]; assert!(RequestFrame::decode(&buf).is_err());
}
#[test]
fn request_truncated_payload() {
let mut buf = BytesMut::new();
buf.put_u32_le(104); buf.put_u32_le(1); buf.put_slice(&[0u8; 50]); assert!(RequestFrame::decode(&buf).is_err());
}
#[test]
fn request_length_too_small() {
let mut buf = BytesMut::new();
buf.put_u32_le(3); buf.put_u32_le(1);
assert!(RequestFrame::decode(&buf).is_err());
}
#[test]
fn request_encoded_size() {
assert_eq!(RequestFrame::encoded_size(0), Some(8));
assert_eq!(RequestFrame::encoded_size(100), Some(108));
assert_eq!(RequestFrame::encoded_size(usize::MAX), None);
}
#[test]
fn response_ok_roundtrip() {
let payload = b"response data";
let mut buf = BytesMut::with_capacity(ResponseFrame::encoded_size(payload.len()).unwrap());
ResponseFrame::encode_ok(payload, &mut buf).unwrap();
let (frame, consumed) = ResponseFrame::decode(&buf).unwrap();
assert_eq!(consumed, buf.len());
assert!(frame.is_ok());
assert_eq!(frame.status, 0);
assert_eq!(frame.payload, payload);
}
#[test]
fn response_ok_empty_payload() {
let mut buf = BytesMut::new();
ResponseFrame::encode_ok(&[], &mut buf).unwrap();
let (frame, consumed) = ResponseFrame::decode(&buf).unwrap();
assert_eq!(consumed, 8);
assert!(frame.is_ok());
assert!(frame.payload.is_empty());
}
#[test]
fn response_error_roundtrip() {
let mut buf = BytesMut::new();
ResponseFrame::encode_error(NonZeroU32::new(1001).unwrap(), &mut buf);
let (frame, consumed) = ResponseFrame::decode(&buf).unwrap();
assert_eq!(consumed, 8);
assert!(!frame.is_ok());
assert_eq!(frame.status, 1001);
assert!(frame.payload.is_empty());
}
#[test]
fn response_truncated_header() {
let buf = [0u8; 7];
assert!(ResponseFrame::decode(&buf).is_err());
}
#[test]
fn response_truncated_payload() {
let mut buf = BytesMut::new();
buf.put_u32_le(0); buf.put_u32_le(100); buf.put_slice(&[0u8; 50]); assert!(ResponseFrame::decode(&buf).is_err());
}
#[test]
fn response_encoded_size() {
assert_eq!(ResponseFrame::encoded_size(0), Some(8));
assert_eq!(ResponseFrame::encoded_size(256), Some(264));
assert_eq!(ResponseFrame::encoded_size(usize::MAX), None);
}
}