1use crate::header::MessageHeader;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum DecodeError {
10 BufferTooShort {
12 required: usize,
14 available: usize,
16 },
17 TemplateMismatch {
19 expected: u16,
21 actual: u16,
23 },
24 SchemaMismatch {
26 expected: u16,
28 actual: u16,
30 },
31 InvalidEnumValue {
33 tag: u16,
35 value: u64,
37 },
38 InvalidUtf8 {
40 offset: usize,
42 },
43 UnsupportedVersion {
45 version: u16,
47 min_supported: u16,
49 },
50}
51
52impl std::fmt::Display for DecodeError {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 match self {
55 Self::BufferTooShort {
56 required,
57 available,
58 } => {
59 write!(
60 f,
61 "buffer too short: required {} bytes, available {} bytes",
62 required, available
63 )
64 }
65 Self::TemplateMismatch { expected, actual } => {
66 write!(
67 f,
68 "template mismatch: expected {}, actual {}",
69 expected, actual
70 )
71 }
72 Self::SchemaMismatch { expected, actual } => {
73 write!(
74 f,
75 "schema mismatch: expected {}, actual {}",
76 expected, actual
77 )
78 }
79 Self::InvalidEnumValue { tag, value } => {
80 write!(f, "invalid enum value: tag {}, value {}", tag, value)
81 }
82 Self::InvalidUtf8 { offset } => {
83 write!(f, "invalid UTF-8 at offset {}", offset)
84 }
85 Self::UnsupportedVersion {
86 version,
87 min_supported,
88 } => {
89 write!(
90 f,
91 "unsupported version: {} (min supported: {})",
92 version, min_supported
93 )
94 }
95 }
96 }
97}
98
99impl std::error::Error for DecodeError {}
100
101pub trait SbeDecoder<'a>: Sized {
117 const TEMPLATE_ID: u16;
119
120 const SCHEMA_ID: u16;
122
123 const SCHEMA_VERSION: u16;
125
126 const BLOCK_LENGTH: u16;
128
129 fn wrap(buffer: &'a [u8], offset: usize, acting_version: u16) -> Self;
139
140 fn encoded_length(&self) -> usize;
144
145 fn validate_header(header: &MessageHeader) -> Result<(), DecodeError> {
153 if header.template_id != Self::TEMPLATE_ID {
154 return Err(DecodeError::TemplateMismatch {
155 expected: Self::TEMPLATE_ID,
156 actual: header.template_id,
157 });
158 }
159 if header.schema_id != Self::SCHEMA_ID {
160 return Err(DecodeError::SchemaMismatch {
161 expected: Self::SCHEMA_ID,
162 actual: header.schema_id,
163 });
164 }
165 Ok(())
166 }
167
168 fn decode(buffer: &'a [u8]) -> Result<Self, DecodeError> {
176 if buffer.len() < MessageHeader::ENCODED_LENGTH {
177 return Err(DecodeError::BufferTooShort {
178 required: MessageHeader::ENCODED_LENGTH,
179 available: buffer.len(),
180 });
181 }
182
183 let header = MessageHeader::wrap(buffer, 0);
184 Self::validate_header(&header)?;
185
186 let required_len = MessageHeader::ENCODED_LENGTH + header.block_length as usize;
187 if buffer.len() < required_len {
188 return Err(DecodeError::BufferTooShort {
189 required: required_len,
190 available: buffer.len(),
191 });
192 }
193
194 Ok(Self::wrap(
195 buffer,
196 MessageHeader::ENCODED_LENGTH,
197 header.version,
198 ))
199 }
200}
201
202pub trait MessageDispatch {
207 fn dispatch(&self, header: &MessageHeader, buffer: &[u8]) -> Result<(), DecodeError>;
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221 use crate::buffer::{AlignedBuffer, ReadBuffer};
222
223 #[test]
224 fn test_decode_error_display_buffer_too_short() {
225 let err = DecodeError::BufferTooShort {
226 required: 100,
227 available: 50,
228 };
229 let msg = err.to_string();
230 assert!(msg.contains("100"));
231 assert!(msg.contains("50"));
232 assert!(msg.contains("buffer too short"));
233 }
234
235 #[test]
236 fn test_decode_error_display_template_mismatch() {
237 let err = DecodeError::TemplateMismatch {
238 expected: 1,
239 actual: 2,
240 };
241 let msg = err.to_string();
242 assert!(msg.contains("expected 1"));
243 assert!(msg.contains("actual 2"));
244 assert!(msg.contains("template mismatch"));
245 }
246
247 #[test]
248 fn test_decode_error_display_schema_mismatch() {
249 let err = DecodeError::SchemaMismatch {
250 expected: 100,
251 actual: 200,
252 };
253 let msg = err.to_string();
254 assert!(msg.contains("100"));
255 assert!(msg.contains("200"));
256 assert!(msg.contains("schema mismatch"));
257 }
258
259 #[test]
260 fn test_decode_error_display_invalid_enum() {
261 let err = DecodeError::InvalidEnumValue { tag: 55, value: 99 };
262 let msg = err.to_string();
263 assert!(msg.contains("55"));
264 assert!(msg.contains("99"));
265 assert!(msg.contains("invalid enum"));
266 }
267
268 #[test]
269 fn test_decode_error_display_invalid_utf8() {
270 let err = DecodeError::InvalidUtf8 { offset: 42 };
271 let msg = err.to_string();
272 assert!(msg.contains("42"));
273 assert!(msg.contains("UTF-8"));
274 }
275
276 #[test]
277 fn test_decode_error_display_unsupported_version() {
278 let err = DecodeError::UnsupportedVersion {
279 version: 5,
280 min_supported: 1,
281 };
282 let msg = err.to_string();
283 assert!(msg.contains("5"));
284 assert!(msg.contains("1"));
285 assert!(msg.contains("unsupported version"));
286 }
287
288 #[test]
289 fn test_decode_error_equality() {
290 let err1 = DecodeError::TemplateMismatch {
291 expected: 1,
292 actual: 2,
293 };
294 let err2 = DecodeError::TemplateMismatch {
295 expected: 1,
296 actual: 2,
297 };
298 assert_eq!(err1, err2);
299
300 let err3 = DecodeError::TemplateMismatch {
301 expected: 1,
302 actual: 3,
303 };
304 assert_ne!(err1, err3);
305 }
306
307 #[test]
308 fn test_decode_error_clone() {
309 let err = DecodeError::BufferTooShort {
310 required: 100,
311 available: 50,
312 };
313 let cloned = err.clone();
314 assert_eq!(err, cloned);
315 }
316
317 #[test]
318 fn test_decode_error_debug() {
319 let err = DecodeError::InvalidEnumValue { tag: 1, value: 2 };
320 let debug_str = format!("{:?}", err);
321 assert!(debug_str.contains("InvalidEnumValue"));
322 }
323
324 struct TestDecoder<'a> {
326 buffer: &'a [u8],
327 offset: usize,
328 #[allow(dead_code)]
329 acting_version: u16,
330 }
331
332 impl<'a> SbeDecoder<'a> for TestDecoder<'a> {
333 const TEMPLATE_ID: u16 = 1;
334 const SCHEMA_ID: u16 = 100;
335 const SCHEMA_VERSION: u16 = 1;
336 const BLOCK_LENGTH: u16 = 16;
337
338 fn wrap(buffer: &'a [u8], offset: usize, acting_version: u16) -> Self {
339 Self {
340 buffer,
341 offset,
342 acting_version,
343 }
344 }
345
346 fn encoded_length(&self) -> usize {
347 MessageHeader::ENCODED_LENGTH + Self::BLOCK_LENGTH as usize
348 }
349 }
350
351 #[test]
352 fn test_validate_header_success() {
353 let header = MessageHeader::new(16, 1, 100, 1);
354 assert!(TestDecoder::validate_header(&header).is_ok());
355 }
356
357 #[test]
358 fn test_validate_header_template_mismatch() {
359 let header = MessageHeader::new(16, 99, 100, 1);
360 let result = TestDecoder::validate_header(&header);
361 assert!(matches!(result, Err(DecodeError::TemplateMismatch { .. })));
362 }
363
364 #[test]
365 fn test_validate_header_schema_mismatch() {
366 let header = MessageHeader::new(16, 1, 999, 1);
367 let result = TestDecoder::validate_header(&header);
368 assert!(matches!(result, Err(DecodeError::SchemaMismatch { .. })));
369 }
370
371 #[test]
372 fn test_decode_buffer_too_short_for_header() {
373 let buffer = [0u8; 4]; let result = TestDecoder::decode(&buffer);
375 assert!(matches!(result, Err(DecodeError::BufferTooShort { .. })));
376 }
377
378 #[test]
379 fn test_decode_buffer_too_short_for_message() {
380 let mut buffer = AlignedBuffer::<16>::new();
381 let header = MessageHeader::new(100, 1, 100, 1); header.encode(&mut buffer, 0);
383
384 let result = TestDecoder::decode(buffer.as_slice());
385 assert!(matches!(result, Err(DecodeError::BufferTooShort { .. })));
386 }
387
388 #[test]
389 fn test_decode_success() {
390 let mut buffer = AlignedBuffer::<32>::new();
391 let header = MessageHeader::new(16, 1, 100, 1);
392 header.encode(&mut buffer, 0);
393
394 let result = TestDecoder::decode(buffer.as_slice());
395 assert!(result.is_ok());
396 let decoder = result.unwrap();
397 assert_eq!(decoder.encoded_length(), 24); }
399
400 #[test]
401 fn test_decoder_wrap() {
402 let buffer = [0u8; 32];
403 let decoder = TestDecoder::wrap(&buffer, 8, 1);
404 assert_eq!(decoder.offset, 8);
405 assert_eq!(decoder.buffer.len(), 32);
406 }
407}