1use crate::DCPError;
4
5pub const DCP_MAGIC: u16 = 0xDC01;
7
8#[repr(C, packed)]
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct BinaryMessageEnvelope {
12 pub magic: u16,
14 pub message_type: u8,
16 pub flags: u8,
18 pub payload_len: u32,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24#[repr(u8)]
25pub enum MessageType {
26 Tool = 1,
27 Resource = 2,
28 Prompt = 3,
29 Response = 4,
30 Error = 5,
31 Stream = 6,
32}
33
34impl MessageType {
35 pub fn from_u8(value: u8) -> Option<Self> {
37 match value {
38 1 => Some(Self::Tool),
39 2 => Some(Self::Resource),
40 3 => Some(Self::Prompt),
41 4 => Some(Self::Response),
42 5 => Some(Self::Error),
43 6 => Some(Self::Stream),
44 _ => None,
45 }
46 }
47}
48
49#[allow(non_snake_case)]
51pub mod Flags {
52 pub const STREAMING: u8 = 0b0000_0001;
53 pub const COMPRESSED: u8 = 0b0000_0010;
54 pub const SIGNED: u8 = 0b0000_0100;
55}
56
57impl BinaryMessageEnvelope {
58 pub const SIZE: usize = 8;
60
61 pub fn new(message_type: MessageType, flags: u8, payload_len: u32) -> Self {
63 Self {
64 magic: DCP_MAGIC,
65 message_type: message_type as u8,
66 flags,
67 payload_len,
68 }
69 }
70
71 #[inline(always)]
73 pub fn from_bytes(bytes: &[u8]) -> Result<&Self, DCPError> {
74 if bytes.len() < Self::SIZE {
75 return Err(DCPError::InsufficientData);
76 }
77 let envelope = unsafe { &*(bytes.as_ptr() as *const Self) };
79 if envelope.magic != DCP_MAGIC {
80 return Err(DCPError::InvalidMagic);
81 }
82 Ok(envelope)
83 }
84
85 #[inline(always)]
87 pub fn from_bytes_mut(bytes: &mut [u8]) -> Result<&mut Self, DCPError> {
88 if bytes.len() < Self::SIZE {
89 return Err(DCPError::InsufficientData);
90 }
91 let envelope = unsafe { &mut *(bytes.as_mut_ptr() as *mut Self) };
93 if envelope.magic != DCP_MAGIC {
94 return Err(DCPError::InvalidMagic);
95 }
96 Ok(envelope)
97 }
98
99 #[inline(always)]
101 pub fn as_bytes(&self) -> &[u8] {
102 unsafe { std::slice::from_raw_parts(self as *const Self as *const u8, Self::SIZE) }
104 }
105
106 pub fn get_message_type(&self) -> Option<MessageType> {
108 MessageType::from_u8(self.message_type)
109 }
110
111 #[inline(always)]
113 pub fn is_streaming(&self) -> bool {
114 self.flags & Flags::STREAMING != 0
115 }
116
117 #[inline(always)]
119 pub fn is_compressed(&self) -> bool {
120 self.flags & Flags::COMPRESSED != 0
121 }
122
123 #[inline(always)]
125 pub fn is_signed(&self) -> bool {
126 self.flags & Flags::SIGNED != 0
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn test_envelope_size() {
136 assert_eq!(std::mem::size_of::<BinaryMessageEnvelope>(), 8);
137 }
138
139 #[test]
140 fn test_envelope_round_trip() {
141 let envelope =
142 BinaryMessageEnvelope::new(MessageType::Tool, Flags::STREAMING | Flags::SIGNED, 1024);
143 let bytes = envelope.as_bytes();
144 let parsed = BinaryMessageEnvelope::from_bytes(bytes).unwrap();
145
146 let magic = parsed.magic;
148 let message_type = parsed.message_type;
149 let flags = parsed.flags;
150 let payload_len = parsed.payload_len;
151
152 assert_eq!(magic, DCP_MAGIC);
153 assert_eq!(message_type, MessageType::Tool as u8);
154 assert_eq!(flags, Flags::STREAMING | Flags::SIGNED);
155 assert_eq!(payload_len, 1024);
156 }
157
158 #[test]
159 fn test_invalid_magic() {
160 let mut bytes = [0u8; 8];
161 bytes[0] = 0xFF;
162 bytes[1] = 0xFF;
163 assert_eq!(
164 BinaryMessageEnvelope::from_bytes(&bytes),
165 Err(DCPError::InvalidMagic)
166 );
167 }
168
169 #[test]
170 fn test_insufficient_data() {
171 let bytes = [0u8; 4];
172 assert_eq!(
173 BinaryMessageEnvelope::from_bytes(&bytes),
174 Err(DCPError::InsufficientData)
175 );
176 }
177
178 #[test]
179 fn test_flag_helpers() {
180 let envelope = BinaryMessageEnvelope::new(
181 MessageType::Stream,
182 Flags::STREAMING | Flags::COMPRESSED,
183 0,
184 );
185 assert!(envelope.is_streaming());
186 assert!(envelope.is_compressed());
187 assert!(!envelope.is_signed());
188 }
189}