use crate::mqtt_serde::control_packet::MqttPacket;
use std::io;
#[cfg(feature = "protocol-testing")]
pub mod malformed;
#[cfg(feature = "protocol-testing")]
pub mod test_client;
#[derive(Debug, Clone)]
pub struct RawPacketBuilder {
bytes: Vec<u8>,
}
impl RawPacketBuilder {
pub fn new() -> Self {
Self { bytes: Vec::new() }
}
pub fn from_packet(packet: MqttPacket) -> io::Result<Self> {
let bytes = packet
.to_bytes()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
Ok(Self { bytes })
}
pub fn set_byte(&mut self, index: usize, value: u8) -> &mut Self {
if index < self.bytes.len() {
self.bytes[index] = value;
}
self
}
pub fn set_fixed_header_flags(&mut self, flags: u8) -> &mut Self {
if !self.bytes.is_empty() {
self.bytes[0] = (self.bytes[0] & 0xF0) | (flags & 0x0F);
}
self
}
pub fn set_packet_type(&mut self, packet_type: u8) -> &mut Self {
if !self.bytes.is_empty() {
self.bytes[0] = (packet_type << 4) | (self.bytes[0] & 0x0F);
}
self
}
pub fn corrupt_variable_length(&mut self, position: usize) -> &mut Self {
if position < self.bytes.len() {
self.bytes[position] |= 0x80;
}
self
}
pub fn insert_bytes(&mut self, position: usize, bytes: &[u8]) -> &mut Self {
self.bytes.splice(position..position, bytes.iter().copied());
self
}
pub fn remove_bytes(&mut self, position: usize, count: usize) -> &mut Self {
let end = (position + count).min(self.bytes.len());
self.bytes.drain(position..end);
self
}
pub fn truncate(&mut self, len: usize) -> &mut Self {
self.bytes.truncate(len);
self
}
pub fn append_bytes(&mut self, bytes: &[u8]) -> &mut Self {
self.bytes.extend_from_slice(bytes);
self
}
pub fn bytes_mut(&mut self) -> &mut Vec<u8> {
&mut self.bytes
}
pub fn bytes(&self) -> &[u8] {
&self.bytes
}
pub fn build(self) -> Vec<u8> {
self.bytes
}
pub fn len(&self) -> usize {
self.bytes.len()
}
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
}
impl Default for RawPacketBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mqtt_serde::mqttv5::pingreqv5::MqttPingReq;
#[test]
fn test_new_builder() {
let builder = RawPacketBuilder::new();
assert!(builder.is_empty());
assert_eq!(builder.len(), 0);
}
#[test]
fn test_from_packet() {
let ping = MqttPingReq {};
let builder = RawPacketBuilder::from_packet(MqttPacket::PingReq5(ping)).unwrap();
assert_eq!(builder.len(), 2);
assert_eq!(builder.bytes(), &[0xC0, 0x00]);
}
#[test]
fn test_append_bytes() {
let mut builder = RawPacketBuilder::new();
builder.append_bytes(&[0xC0]);
builder.append_bytes(&[0x00]);
assert_eq!(builder.bytes(), &[0xC0, 0x00]);
}
#[test]
fn test_set_byte() {
let mut builder = RawPacketBuilder::new();
builder.append_bytes(&[0xC0, 0x00]);
builder.set_byte(0, 0xD0);
assert_eq!(builder.bytes()[0], 0xD0);
}
#[test]
fn test_set_fixed_header_flags() {
let mut builder = RawPacketBuilder::new();
builder.append_bytes(&[0xC0, 0x00]);
builder.set_fixed_header_flags(0x0F);
assert_eq!(builder.bytes()[0], 0xCF);
}
#[test]
fn test_set_packet_type() {
let mut builder = RawPacketBuilder::new();
builder.append_bytes(&[0xC0, 0x00]);
builder.set_packet_type(0x0D);
assert_eq!(builder.bytes()[0], 0xD0);
}
#[test]
fn test_corrupt_variable_length() {
let mut builder = RawPacketBuilder::new();
builder.append_bytes(&[0xC0, 0x00]);
builder.corrupt_variable_length(1);
assert_eq!(builder.bytes()[1], 0x80);
}
#[test]
fn test_insert_bytes() {
let mut builder = RawPacketBuilder::new();
builder.append_bytes(&[0xC0, 0x00]);
builder.insert_bytes(1, &[0xFF, 0xFE]);
assert_eq!(builder.bytes(), &[0xC0, 0xFF, 0xFE, 0x00]);
}
#[test]
fn test_remove_bytes() {
let mut builder = RawPacketBuilder::new();
builder.append_bytes(&[0xC0, 0xFF, 0xFE, 0x00]);
builder.remove_bytes(1, 2);
assert_eq!(builder.bytes(), &[0xC0, 0x00]);
}
#[test]
fn test_truncate() {
let mut builder = RawPacketBuilder::new();
builder.append_bytes(&[0xC0, 0x00, 0xFF, 0xFE]);
builder.truncate(2);
assert_eq!(builder.bytes(), &[0xC0, 0x00]);
}
#[test]
fn test_build() {
let mut builder = RawPacketBuilder::new();
builder.append_bytes(&[0xC0, 0x00]);
let packet = builder.build();
assert_eq!(packet, vec![0xC0, 0x00]);
}
}