use bebytes::BeBytes;
#[derive(Debug, Clone, PartialEq, BeBytes)]
struct CoapMessage {
version: u8,
msg_type: u8,
token_length: u8,
code: u8,
message_id: u16,
#[FromField(token_length)]
token: Vec<u8>,
option_count: u8,
#[FromField(option_count)]
#[UntilMarker(0xFF)]
options: Vec<Vec<u8>>,
payload_marker: u8, payload: Vec<u8>, }
#[test]
fn test_coap_like_protocol() {
let msg = CoapMessage {
version: 1,
msg_type: 0,
token_length: 4,
code: 69, message_id: 0x1234,
token: vec![0xAA, 0xBB, 0xCC, 0xDD],
option_count: 3,
options: vec![
vec![0x11, 0x22], vec![0x33, 0x44, 0x55], vec![0x66], ],
payload_marker: 0xFF,
payload: vec![0x48, 0x65, 0x6C, 0x6C, 0x6F], };
let bytes = msg.to_be_bytes();
println!("Serialized {} bytes", bytes.len());
let (parsed, consumed) = CoapMessage::try_from_be_bytes(&bytes).unwrap();
assert_eq!(consumed, bytes.len());
assert_eq!(parsed, msg);
let bytes2 = parsed.to_be_bytes();
assert_eq!(bytes, bytes2);
}
#[derive(Debug, Clone, PartialEq, BeBytes)]
struct OldWayProtocol {
count: u8,
#[UntilMarker(0xAA)]
section1: Vec<u8>,
#[UntilMarker(0xAA)]
section2: Vec<u8>,
#[UntilMarker(0xAA)]
section3: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, BeBytes)]
struct NewWayProtocol {
count: u8,
#[FromField(count)]
#[UntilMarker(0xAA)]
sections: Vec<Vec<u8>>,
}
#[test]
fn test_old_vs_new_pattern() {
let old = OldWayProtocol {
count: 3,
section1: vec![0x01, 0x02],
section2: vec![0x03, 0x04],
section3: vec![0x05, 0x06],
};
let old_bytes = old.to_be_bytes();
let new = NewWayProtocol {
count: 3,
sections: vec![vec![0x01, 0x02], vec![0x03, 0x04], vec![0x05, 0x06]],
};
let new_bytes = new.to_be_bytes();
assert_eq!(old_bytes, new_bytes);
let new_variable = NewWayProtocol {
count: 5,
sections: vec![
vec![0x01],
vec![0x02, 0x03],
vec![0x04],
vec![0x05, 0x06, 0x07],
vec![0x08],
],
};
let var_bytes = new_variable.to_be_bytes();
let (parsed, _) = NewWayProtocol::try_from_be_bytes(&var_bytes).unwrap();
assert_eq!(parsed, new_variable);
}
#[test]
fn test_compile_time_checks_enforced() {
#[derive(Debug, Clone, PartialEq, BeBytes)]
struct ValidStruct {
#[With(size(2))]
#[UntilMarker(0xBB)]
segments: Vec<Vec<u8>>,
}
let valid = ValidStruct {
segments: vec![vec![1, 2], vec![3, 4]],
};
let bytes = valid.to_be_bytes();
let (parsed, _) = ValidStruct::try_from_be_bytes(&bytes).unwrap();
assert_eq!(parsed, valid);
}
#[test]
fn test_performance_characteristics() {
use std::time::Instant;
#[derive(Debug, Clone, PartialEq, BeBytes)]
struct LargeMessage {
segment_count: u8,
#[FromField(segment_count)]
#[UntilMarker(0x00)]
segments: Vec<Vec<u8>>,
}
let mut segments = Vec::new();
for i in 0..100 {
segments.push(vec![(i + 1) as u8; (i % 10 + 1) as usize]);
}
let msg = LargeMessage {
segment_count: 100,
segments,
};
let start = Instant::now();
let bytes = msg.to_be_bytes();
let serialize_time = start.elapsed();
let start = Instant::now();
let (parsed, _) = LargeMessage::try_from_be_bytes(&bytes).unwrap();
let deserialize_time = start.elapsed();
println!("Serialization time: {:?}", serialize_time);
println!("Deserialization time: {:?}", deserialize_time);
assert_eq!(parsed, msg);
assert!(serialize_time.as_millis() < 1);
assert!(deserialize_time.as_millis() < 1);
}
#[derive(Debug, Clone, PartialEq, BeBytes)]
struct IntegratedFeatures {
#[bits(4)]
version: u8,
#[bits(4)]
flags: u8,
name_len: u8,
#[FromField(name_len)]
name: String,
segment_count: u8,
#[FromField(segment_count)]
#[UntilMarker(0xEE)]
data_segments: Vec<Vec<u8>>,
checksum: u32,
}
#[test]
fn test_integration_with_other_features() {
let msg = IntegratedFeatures {
version: 2,
flags: 0b1010,
name_len: 5,
name: "Hello".to_string(),
segment_count: 2,
data_segments: vec![vec![0x11, 0x22], vec![0x33, 0x44, 0x55]],
checksum: 0x12345678,
};
let bytes = msg.to_be_bytes();
let (parsed, consumed) = IntegratedFeatures::try_from_be_bytes(&bytes).unwrap();
assert_eq!(consumed, bytes.len());
assert_eq!(parsed.version, 2);
assert_eq!(parsed.flags, 0b1010);
assert_eq!(parsed.name, "Hello");
assert_eq!(parsed.data_segments.len(), 2);
assert_eq!(parsed.checksum, 0x12345678);
assert_eq!(parsed, msg);
}