use crate::constants::{MAGIC, MAX_PAYLOAD_SIZE, VERSION};
use crate::error::ProtocolError;
use crate::frame::{Frame, FrameHeader};
use crate::types::{FrameFlags, MessageType, ProtocolErrorKind, RequestId};
pub fn encode(
msg_type: MessageType,
flags: FrameFlags,
request_id: RequestId,
payload: &[u8],
) -> Result<Vec<u8>, ProtocolError> {
if payload.len() > MAX_PAYLOAD_SIZE {
return Err(ProtocolError::new(ProtocolErrorKind::PayloadTooLarge));
}
let mut buf = Vec::with_capacity(FrameHeader::SIZE + payload.len());
buf.extend_from_slice(&MAGIC.to_le_bytes());
buf.extend_from_slice(&VERSION.to_le_bytes());
buf.push(msg_type as u8);
buf.push(flags.bits());
buf.extend_from_slice(&request_id.0.to_le_bytes());
buf.extend_from_slice(&u32::try_from(payload.len()).unwrap().to_le_bytes());
buf.extend_from_slice(payload);
debug_assert_eq!(buf.len(), FrameHeader::SIZE + payload.len());
Ok(buf)
}
pub fn decode(buffer: &[u8]) -> Result<Frame<'_>, ProtocolError> {
if buffer.len() < FrameHeader::SIZE {
return Err(ProtocolError::new(ProtocolErrorKind::MalformedFrame));
}
let magic = u32::from_le_bytes(buffer[0..4].try_into().unwrap());
if magic != MAGIC {
return Err(ProtocolError::new(ProtocolErrorKind::InvalidMagic));
}
let version = u16::from_le_bytes(buffer[4..6].try_into().unwrap());
if version != VERSION {
return Err(ProtocolError::new(ProtocolErrorKind::UnsupportedVersion));
}
let msg_type = MessageType::try_from(buffer[6])
.map_err(|()| ProtocolError::new(ProtocolErrorKind::UnknownMessageType))?;
let flags = FrameFlags::from_bits_truncate(buffer[7]);
let request_id = RequestId(u64::from_le_bytes(buffer[8..16].try_into().unwrap()));
let payload_length_u32 = u32::from_le_bytes(buffer[16..20].try_into().unwrap());
let payload_length = payload_length_u32 as usize;
if payload_length > MAX_PAYLOAD_SIZE {
return Err(ProtocolError::new(ProtocolErrorKind::PayloadTooLarge));
}
let total_len = FrameHeader::SIZE + payload_length;
if buffer.len() < total_len {
return Err(ProtocolError::new(ProtocolErrorKind::MalformedFrame));
}
let payload = &buffer[FrameHeader::SIZE..total_len];
Ok(Frame {
header: FrameHeader {
magic,
version,
msg_type: msg_type as u8,
flags: flags.bits(),
request_id: request_id.0,
payload_length: payload_length_u32,
},
payload,
})
}