1#![allow(clippy::approx_constant)]
2
3use super::error::BinaryError;
9use super::types::TypeTag;
10use super::varint;
11use lnmp_core::{LnmpField, LnmpRecord, LnmpValue};
12
13#[derive(Debug, Clone)]
15pub struct NestedDecoderConfig {
16 pub allow_nested: bool,
18 pub validate_nesting: bool,
20 pub max_depth: usize,
22}
23
24impl Default for NestedDecoderConfig {
25 fn default() -> Self {
26 Self {
27 allow_nested: true,
28 validate_nesting: false,
29 max_depth: 32,
30 }
31 }
32}
33
34impl NestedDecoderConfig {
35 pub fn new() -> Self {
37 Self::default()
38 }
39
40 pub fn with_allow_nested(mut self, allow: bool) -> Self {
42 self.allow_nested = allow;
43 self
44 }
45
46 pub fn with_validate_nesting(mut self, validate: bool) -> Self {
48 self.validate_nesting = validate;
49 self
50 }
51
52 pub fn with_max_depth(mut self, max_depth: usize) -> Self {
54 self.max_depth = max_depth;
55 self
56 }
57}
58
59#[derive(Debug)]
63pub struct BinaryNestedDecoder {
64 config: NestedDecoderConfig,
65}
66
67impl BinaryNestedDecoder {
68 pub fn new() -> Self {
70 Self {
71 config: NestedDecoderConfig::default(),
72 }
73 }
74
75 pub fn with_config(config: NestedDecoderConfig) -> Self {
77 Self { config }
78 }
79
80 pub fn decode_nested_record(&self, bytes: &[u8]) -> Result<(LnmpRecord, usize), BinaryError> {
105 self.decode_nested_record_with_depth(bytes, 0)
106 }
107
108 fn decode_nested_record_with_depth(
110 &self,
111 bytes: &[u8],
112 current_depth: usize,
113 ) -> Result<(LnmpRecord, usize), BinaryError> {
114 if current_depth >= self.config.max_depth {
116 return Err(BinaryError::NestingDepthExceeded {
117 depth: current_depth,
118 max: self.config.max_depth,
119 });
120 }
121
122 let mut offset = 0;
123
124 if bytes.is_empty() {
126 return Err(BinaryError::UnexpectedEof {
127 expected: 1,
128 found: bytes.len(),
129 });
130 }
131
132 let tag = bytes[offset];
133 offset += 1;
134
135 if tag != TypeTag::NestedRecord.to_u8() {
136 return Err(BinaryError::InvalidTypeTag { tag });
137 }
138
139 let (field_count, consumed) = varint::decode(&bytes[offset..])?;
141 offset += consumed;
142
143 if field_count < 0 {
144 return Err(BinaryError::InvalidNestedStructure {
145 reason: format!("Negative field count: {}", field_count),
146 });
147 }
148
149 let field_count = field_count as usize;
150
151 let mut record = LnmpRecord::new();
153
154 for _ in 0..field_count {
155 let (fid_i64, consumed) = varint::decode(&bytes[offset..])?;
157 offset += consumed;
158
159 if fid_i64 < 0 || fid_i64 > u16::MAX as i64 {
160 return Err(BinaryError::InvalidFID {
161 fid: fid_i64 as u16,
162 reason: format!("FID out of range: {}", fid_i64),
163 });
164 }
165
166 let fid = fid_i64 as u16;
167
168 let (value, consumed) = self.decode_value_recursive(&bytes[offset..], current_depth)?;
170 offset += consumed;
171
172 record.add_field(LnmpField { fid, value });
174 }
175
176 Ok((record, offset))
177 }
178
179 fn decode_value_recursive(
181 &self,
182 bytes: &[u8],
183 current_depth: usize,
184 ) -> Result<(LnmpValue, usize), BinaryError> {
185 if current_depth >= self.config.max_depth {
187 return Err(BinaryError::NestingDepthExceeded {
188 depth: current_depth,
189 max: self.config.max_depth,
190 });
191 }
192
193 let mut offset = 0;
194
195 if bytes.is_empty() {
197 return Err(BinaryError::UnexpectedEof {
198 expected: 1,
199 found: bytes.len(),
200 });
201 }
202
203 let tag_byte = bytes[offset];
204 offset += 1;
205
206 let type_tag = TypeTag::from_u8(tag_byte)?;
207
208 match type_tag {
209 TypeTag::Int => {
210 let (value, consumed) = varint::decode(&bytes[offset..])?;
211 offset += consumed;
212 Ok((LnmpValue::Int(value), offset))
213 }
214 TypeTag::Float => {
215 if bytes.len() < offset + 8 {
216 return Err(BinaryError::UnexpectedEof {
217 expected: offset + 8,
218 found: bytes.len(),
219 });
220 }
221 let float_bytes: [u8; 8] = bytes[offset..offset + 8].try_into().unwrap();
222 let value = f64::from_le_bytes(float_bytes);
223 offset += 8;
224 Ok((LnmpValue::Float(value), offset))
225 }
226 TypeTag::Bool => {
227 if bytes.len() < offset + 1 {
228 return Err(BinaryError::UnexpectedEof {
229 expected: offset + 1,
230 found: bytes.len(),
231 });
232 }
233 let value = bytes[offset] != 0x00;
234 offset += 1;
235 Ok((LnmpValue::Bool(value), offset))
236 }
237 TypeTag::String => {
238 let (length, consumed) = varint::decode(&bytes[offset..])?;
239 offset += consumed;
240
241 if length < 0 {
242 return Err(BinaryError::InvalidValue {
243 field_id: 0,
244 type_tag: tag_byte,
245 reason: format!("Negative string length: {}", length),
246 });
247 }
248
249 let length = length as usize;
250
251 if bytes.len() < offset + length {
252 return Err(BinaryError::UnexpectedEof {
253 expected: offset + length,
254 found: bytes.len(),
255 });
256 }
257
258 let string_bytes = &bytes[offset..offset + length];
259 let value = String::from_utf8(string_bytes.to_vec())
260 .map_err(|_| BinaryError::InvalidUtf8 { field_id: 0 })?;
261 offset += length;
262 Ok((LnmpValue::String(value), offset))
263 }
264 TypeTag::StringArray => {
265 let (count, consumed) = varint::decode(&bytes[offset..])?;
266 offset += consumed;
267
268 if count < 0 {
269 return Err(BinaryError::InvalidValue {
270 field_id: 0,
271 type_tag: tag_byte,
272 reason: format!("Negative array count: {}", count),
273 });
274 }
275
276 let count = count as usize;
277 let mut array = Vec::with_capacity(count);
278
279 for _ in 0..count {
280 let (length, consumed) = varint::decode(&bytes[offset..])?;
281 offset += consumed;
282
283 if length < 0 {
284 return Err(BinaryError::InvalidValue {
285 field_id: 0,
286 type_tag: tag_byte,
287 reason: format!("Negative string length in array: {}", length),
288 });
289 }
290
291 let length = length as usize;
292
293 if bytes.len() < offset + length {
294 return Err(BinaryError::UnexpectedEof {
295 expected: offset + length,
296 found: bytes.len(),
297 });
298 }
299
300 let string_bytes = &bytes[offset..offset + length];
301 let string = String::from_utf8(string_bytes.to_vec())
302 .map_err(|_| BinaryError::InvalidUtf8 { field_id: 0 })?;
303 offset += length;
304 array.push(string);
305 }
306
307 Ok((LnmpValue::StringArray(array), offset))
308 }
309 TypeTag::NestedRecord => {
310 if !self.config.allow_nested {
312 return Err(BinaryError::NestedStructureNotSupported);
313 }
314
315 let (record, consumed) =
318 self.decode_nested_record_with_depth(&bytes[offset - 1..], current_depth + 1)?;
319 offset += consumed - 1;
321 Ok((LnmpValue::NestedRecord(Box::new(record)), offset))
322 }
323 TypeTag::NestedArray => {
324 if !self.config.allow_nested {
326 return Err(BinaryError::NestedStructureNotSupported);
327 }
328
329 let (records, consumed) =
332 self.decode_nested_array_with_depth(&bytes[offset - 1..], current_depth + 1)?;
333 offset += consumed - 1;
335 Ok((LnmpValue::NestedArray(records), offset))
336 }
337 _ => Err(BinaryError::InvalidTypeTag { tag: tag_byte }),
338 }
339 }
340
341 pub fn decode_nested_array(
366 &self,
367 bytes: &[u8],
368 ) -> Result<(Vec<LnmpRecord>, usize), BinaryError> {
369 self.decode_nested_array_with_depth(bytes, 0)
370 }
371
372 fn decode_nested_array_with_depth(
374 &self,
375 bytes: &[u8],
376 current_depth: usize,
377 ) -> Result<(Vec<LnmpRecord>, usize), BinaryError> {
378 if current_depth >= self.config.max_depth {
380 return Err(BinaryError::NestingDepthExceeded {
381 depth: current_depth,
382 max: self.config.max_depth,
383 });
384 }
385
386 let mut offset = 0;
387
388 if bytes.is_empty() {
390 return Err(BinaryError::UnexpectedEof {
391 expected: 1,
392 found: bytes.len(),
393 });
394 }
395
396 let tag = bytes[offset];
397 offset += 1;
398
399 if tag != TypeTag::NestedArray.to_u8() {
400 return Err(BinaryError::InvalidTypeTag { tag });
401 }
402
403 let (element_count, consumed) = varint::decode(&bytes[offset..])?;
405 offset += consumed;
406
407 if element_count < 0 {
408 return Err(BinaryError::InvalidNestedStructure {
409 reason: format!("Negative element count: {}", element_count),
410 });
411 }
412
413 let element_count = element_count as usize;
414
415 let mut records = Vec::with_capacity(element_count);
417
418 for _ in 0..element_count {
419 let (record, consumed) =
422 self.decode_nested_record_with_depth(&bytes[offset..], current_depth)?;
423 offset += consumed;
424 records.push(record);
425 }
426
427 Ok((records, offset))
428 }
429}
430
431impl Default for BinaryNestedDecoder {
432 fn default() -> Self {
433 Self::new()
434 }
435}
436
437#[cfg(test)]
438mod tests {
439 #![allow(clippy::approx_constant)]
440
441 use super::*;
442
443 #[test]
444 fn test_nested_decoder_config_default() {
445 let config = NestedDecoderConfig::default();
446 assert!(config.allow_nested);
447 assert!(!config.validate_nesting);
448 assert_eq!(config.max_depth, 32);
449 }
450
451 #[test]
452 fn test_nested_decoder_config_builder() {
453 let config = NestedDecoderConfig::new()
454 .with_allow_nested(false)
455 .with_validate_nesting(true)
456 .with_max_depth(16);
457
458 assert!(!config.allow_nested);
459 assert!(config.validate_nesting);
460 assert_eq!(config.max_depth, 16);
461 }
462
463 #[test]
464 fn test_nested_decoder_new() {
465 let decoder = BinaryNestedDecoder::new();
466 assert!(decoder.config.allow_nested);
467 assert!(!decoder.config.validate_nesting);
468 assert_eq!(decoder.config.max_depth, 32);
469 }
470
471 #[test]
472 fn test_nested_decoder_with_config() {
473 let config = NestedDecoderConfig::new()
474 .with_max_depth(8)
475 .with_validate_nesting(true);
476
477 let decoder = BinaryNestedDecoder::with_config(config);
478 assert_eq!(decoder.config.max_depth, 8);
479 assert!(decoder.config.validate_nesting);
480 }
481
482 #[test]
483 fn test_nested_decoder_default() {
484 let decoder = BinaryNestedDecoder::default();
485 assert!(decoder.config.allow_nested);
486 assert_eq!(decoder.config.max_depth, 32);
487 }
488}
489
490#[test]
491fn test_decode_empty_nested_record() {
492 use crate::binary::nested_encoder::BinaryNestedEncoder;
493
494 let encoder = BinaryNestedEncoder::new();
495 let record = LnmpRecord::new();
496 let binary = encoder.encode_nested_record(&record).unwrap();
497
498 let decoder = BinaryNestedDecoder::new();
499 let (decoded, consumed) = decoder.decode_nested_record(&binary).unwrap();
500
501 assert_eq!(decoded.fields().len(), 0);
502 assert_eq!(consumed, binary.len());
503}
504
505#[test]
506fn test_decode_single_level_nested_record() {
507 use crate::binary::nested_encoder::BinaryNestedEncoder;
508
509 let encoder = BinaryNestedEncoder::new();
510 let mut record = LnmpRecord::new();
511 record.add_field(LnmpField {
512 fid: 1,
513 value: LnmpValue::Int(42),
514 });
515 record.add_field(LnmpField {
516 fid: 2,
517 value: LnmpValue::String("test".to_string()),
518 });
519
520 let binary = encoder.encode_nested_record(&record).unwrap();
521
522 let decoder = BinaryNestedDecoder::new();
523 let (decoded, consumed) = decoder.decode_nested_record(&binary).unwrap();
524
525 assert_eq!(decoded.fields().len(), 2);
526 assert_eq!(decoded.get_field(1).unwrap().value, LnmpValue::Int(42));
527 assert_eq!(
528 decoded.get_field(2).unwrap().value,
529 LnmpValue::String("test".to_string())
530 );
531 assert_eq!(consumed, binary.len());
532}
533
534#[test]
535fn test_decode_nested_record_invalid_tag() {
536 let decoder = BinaryNestedDecoder::new();
537 let bytes = vec![0x01, 0x00]; let result = decoder.decode_nested_record(&bytes);
540 assert!(matches!(
541 result,
542 Err(BinaryError::InvalidTypeTag { tag: 0x01 })
543 ));
544}
545
546#[test]
547fn test_decode_nested_record_insufficient_data() {
548 let decoder = BinaryNestedDecoder::new();
549 let bytes = vec![]; let result = decoder.decode_nested_record(&bytes);
552 assert!(matches!(result, Err(BinaryError::UnexpectedEof { .. })));
553}
554
555#[test]
556fn test_decode_nested_record_negative_field_count() {
557 let decoder = BinaryNestedDecoder::new();
558 let bytes = vec![0x06, 0x7F]; let result = decoder.decode_nested_record(&bytes);
562 assert!(matches!(
563 result,
564 Err(BinaryError::InvalidNestedStructure { .. })
565 ));
566}
567
568#[test]
569fn test_decode_multi_level_nested_record() {
570 use crate::binary::nested_encoder::BinaryNestedEncoder;
571
572 let encoder = BinaryNestedEncoder::new();
573
574 let mut inner_record = LnmpRecord::new();
576 inner_record.add_field(LnmpField {
577 fid: 1,
578 value: LnmpValue::Int(42),
579 });
580
581 let mut outer_record = LnmpRecord::new();
582 outer_record.add_field(LnmpField {
583 fid: 2,
584 value: LnmpValue::NestedRecord(Box::new(inner_record)),
585 });
586
587 let binary = encoder.encode_nested_record(&outer_record).unwrap();
588
589 let decoder = BinaryNestedDecoder::new();
590 let (decoded, consumed) = decoder.decode_nested_record(&binary).unwrap();
591
592 assert_eq!(decoded.fields().len(), 1);
593 assert_eq!(consumed, binary.len());
594
595 match &decoded.get_field(2).unwrap().value {
597 LnmpValue::NestedRecord(inner) => {
598 assert_eq!(inner.fields().len(), 1);
599 assert_eq!(inner.get_field(1).unwrap().value, LnmpValue::Int(42));
600 }
601 _ => panic!("Expected NestedRecord"),
602 }
603}
604
605#[test]
606fn test_decode_nested_record_depth_limit() {
607 use crate::binary::nested_encoder::BinaryNestedEncoder;
608
609 let config = NestedDecoderConfig::new().with_max_depth(2);
610 let decoder = BinaryNestedDecoder::with_config(config);
611
612 let encoder = BinaryNestedEncoder::new();
613
614 let mut level3 = LnmpRecord::new();
616 level3.add_field(LnmpField {
617 fid: 1,
618 value: LnmpValue::Int(42),
619 });
620
621 let mut level2 = LnmpRecord::new();
622 level2.add_field(LnmpField {
623 fid: 2,
624 value: LnmpValue::NestedRecord(Box::new(level3)),
625 });
626
627 let mut level1 = LnmpRecord::new();
628 level1.add_field(LnmpField {
629 fid: 3,
630 value: LnmpValue::NestedRecord(Box::new(level2)),
631 });
632
633 let binary = encoder.encode_nested_record(&level1).unwrap();
634
635 let result = decoder.decode_nested_record(&binary);
636 assert!(matches!(
637 result,
638 Err(BinaryError::NestingDepthExceeded { .. })
639 ));
640}
641
642#[test]
643fn test_decode_nested_record_all_primitive_types() {
644 use crate::binary::nested_encoder::BinaryNestedEncoder;
645
646 let encoder = BinaryNestedEncoder::new();
647
648 let mut record = LnmpRecord::new();
649 record.add_field(LnmpField {
650 fid: 1,
651 value: LnmpValue::Int(-42),
652 });
653 record.add_field(LnmpField {
654 fid: 2,
655 value: LnmpValue::Float(3.14),
656 });
657 record.add_field(LnmpField {
658 fid: 3,
659 value: LnmpValue::Bool(false),
660 });
661 record.add_field(LnmpField {
662 fid: 4,
663 value: LnmpValue::String("test".to_string()),
664 });
665 record.add_field(LnmpField {
666 fid: 5,
667 value: LnmpValue::StringArray(vec!["a".to_string(), "b".to_string()]),
668 });
669
670 let binary = encoder.encode_nested_record(&record).unwrap();
671
672 let decoder = BinaryNestedDecoder::new();
673 let (decoded, _) = decoder.decode_nested_record(&binary).unwrap();
674
675 assert_eq!(decoded.get_field(1).unwrap().value, LnmpValue::Int(-42));
676 assert_eq!(decoded.get_field(2).unwrap().value, LnmpValue::Float(3.14));
677 assert_eq!(decoded.get_field(3).unwrap().value, LnmpValue::Bool(false));
678 assert_eq!(
679 decoded.get_field(4).unwrap().value,
680 LnmpValue::String("test".to_string())
681 );
682 assert_eq!(
683 decoded.get_field(5).unwrap().value,
684 LnmpValue::StringArray(vec!["a".to_string(), "b".to_string()])
685 );
686}
687
688#[test]
689fn test_decode_nested_record_roundtrip() {
690 use crate::binary::nested_encoder::BinaryNestedEncoder;
691
692 let encoder = BinaryNestedEncoder::new();
693 let decoder = BinaryNestedDecoder::new();
694
695 let mut original = LnmpRecord::new();
696 original.add_field(LnmpField {
697 fid: 1,
698 value: LnmpValue::Int(100),
699 });
700 original.add_field(LnmpField {
701 fid: 2,
702 value: LnmpValue::String("hello".to_string()),
703 });
704
705 let binary = encoder.encode_nested_record(&original).unwrap();
706 let (decoded, _) = decoder.decode_nested_record(&binary).unwrap();
707
708 assert_eq!(original.sorted_fields(), decoded.sorted_fields());
710}
711
712#[test]
713fn test_decode_empty_nested_array() {
714 use crate::binary::nested_encoder::BinaryNestedEncoder;
715
716 let encoder = BinaryNestedEncoder::new();
717 let records: Vec<LnmpRecord> = vec![];
718 let binary = encoder.encode_nested_array(&records).unwrap();
719
720 let decoder = BinaryNestedDecoder::new();
721 let (decoded, consumed) = decoder.decode_nested_array(&binary).unwrap();
722
723 assert_eq!(decoded.len(), 0);
724 assert_eq!(consumed, binary.len());
725}
726
727#[test]
728fn test_decode_nested_array_single_record() {
729 use crate::binary::nested_encoder::BinaryNestedEncoder;
730
731 let encoder = BinaryNestedEncoder::new();
732 let mut record = LnmpRecord::new();
733 record.add_field(LnmpField {
734 fid: 1,
735 value: LnmpValue::Int(42),
736 });
737
738 let binary = encoder.encode_nested_array(&[record.clone()]).unwrap();
739
740 let decoder = BinaryNestedDecoder::new();
741 let (decoded, consumed) = decoder.decode_nested_array(&binary).unwrap();
742
743 assert_eq!(decoded.len(), 1);
744 assert_eq!(decoded[0].sorted_fields(), record.sorted_fields());
745 assert_eq!(consumed, binary.len());
746}
747
748#[test]
749fn test_decode_nested_array_multiple_records() {
750 use crate::binary::nested_encoder::BinaryNestedEncoder;
751
752 let encoder = BinaryNestedEncoder::new();
753
754 let mut record1 = LnmpRecord::new();
755 record1.add_field(LnmpField {
756 fid: 1,
757 value: LnmpValue::Int(1),
758 });
759
760 let mut record2 = LnmpRecord::new();
761 record2.add_field(LnmpField {
762 fid: 1,
763 value: LnmpValue::Int(2),
764 });
765
766 let binary = encoder
767 .encode_nested_array(&[record1.clone(), record2.clone()])
768 .unwrap();
769
770 let decoder = BinaryNestedDecoder::new();
771 let (decoded, consumed) = decoder.decode_nested_array(&binary).unwrap();
772
773 assert_eq!(decoded.len(), 2);
774 assert_eq!(decoded[0].sorted_fields(), record1.sorted_fields());
775 assert_eq!(decoded[1].sorted_fields(), record2.sorted_fields());
776 assert_eq!(consumed, binary.len());
777}
778
779#[test]
780fn test_decode_nested_array_invalid_tag() {
781 let decoder = BinaryNestedDecoder::new();
782 let bytes = vec![0x06, 0x00]; let result = decoder.decode_nested_array(&bytes);
785 assert!(matches!(
786 result,
787 Err(BinaryError::InvalidTypeTag { tag: 0x06 })
788 ));
789}
790
791#[test]
792fn test_decode_nested_array_negative_element_count() {
793 let decoder = BinaryNestedDecoder::new();
794 let bytes = vec![0x07, 0x7F]; let result = decoder.decode_nested_array(&bytes);
798 assert!(matches!(
799 result,
800 Err(BinaryError::InvalidNestedStructure { .. })
801 ));
802}
803
804#[test]
805fn test_decode_nested_array_depth_limit() {
806 use crate::binary::nested_encoder::BinaryNestedEncoder;
807
808 let config = NestedDecoderConfig::new().with_max_depth(2);
809 let decoder = BinaryNestedDecoder::with_config(config);
810
811 let encoder = BinaryNestedEncoder::new();
812
813 let mut level3 = LnmpRecord::new();
815 level3.add_field(LnmpField {
816 fid: 1,
817 value: LnmpValue::Int(42),
818 });
819
820 let mut level2 = LnmpRecord::new();
821 level2.add_field(LnmpField {
822 fid: 2,
823 value: LnmpValue::NestedRecord(Box::new(level3)),
824 });
825
826 let mut level1 = LnmpRecord::new();
827 level1.add_field(LnmpField {
828 fid: 3,
829 value: LnmpValue::NestedArray(vec![level2]),
830 });
831
832 let binary = encoder.encode_nested_record(&level1).unwrap();
833
834 let result = decoder.decode_nested_record(&binary);
835 assert!(matches!(
836 result,
837 Err(BinaryError::NestingDepthExceeded { .. })
838 ));
839}
840
841#[test]
842fn test_decode_nested_array_roundtrip() {
843 use crate::binary::nested_encoder::BinaryNestedEncoder;
844
845 let encoder = BinaryNestedEncoder::new();
846 let decoder = BinaryNestedDecoder::new();
847
848 let mut record1 = LnmpRecord::new();
849 record1.add_field(LnmpField {
850 fid: 1,
851 value: LnmpValue::String("first".to_string()),
852 });
853
854 let mut record2 = LnmpRecord::new();
855 record2.add_field(LnmpField {
856 fid: 2,
857 value: LnmpValue::String("second".to_string()),
858 });
859
860 let original = vec![record1.clone(), record2.clone()];
861
862 let binary = encoder.encode_nested_array(&original).unwrap();
863 let (decoded, _) = decoder.decode_nested_array(&binary).unwrap();
864
865 assert_eq!(decoded.len(), original.len());
866 for (i, record) in decoded.iter().enumerate() {
867 assert_eq!(record.sorted_fields(), original[i].sorted_fields());
868 }
869}
870
871#[test]
872fn test_decode_deeply_nested_record_depth_2() {
873 use crate::binary::nested_encoder::BinaryNestedEncoder;
874
875 let encoder = BinaryNestedEncoder::new();
876 let decoder = BinaryNestedDecoder::new();
877
878 let mut level2 = LnmpRecord::new();
880 level2.add_field(LnmpField {
881 fid: 1,
882 value: LnmpValue::Int(100),
883 });
884
885 let mut level1 = LnmpRecord::new();
886 level1.add_field(LnmpField {
887 fid: 2,
888 value: LnmpValue::NestedRecord(Box::new(level2)),
889 });
890
891 let binary = encoder.encode_nested_record(&level1).unwrap();
892 let (decoded, _) = decoder.decode_nested_record(&binary).unwrap();
893
894 match &decoded.get_field(2).unwrap().value {
896 LnmpValue::NestedRecord(inner) => {
897 assert_eq!(inner.get_field(1).unwrap().value, LnmpValue::Int(100));
898 }
899 _ => panic!("Expected NestedRecord"),
900 }
901}
902
903#[test]
904fn test_decode_deeply_nested_record_depth_3() {
905 use crate::binary::nested_encoder::BinaryNestedEncoder;
906
907 let encoder = BinaryNestedEncoder::new();
908 let decoder = BinaryNestedDecoder::new();
909
910 let mut level3 = LnmpRecord::new();
912 level3.add_field(LnmpField {
913 fid: 1,
914 value: LnmpValue::Int(100),
915 });
916
917 let mut level2 = LnmpRecord::new();
918 level2.add_field(LnmpField {
919 fid: 2,
920 value: LnmpValue::NestedRecord(Box::new(level3)),
921 });
922
923 let mut level1 = LnmpRecord::new();
924 level1.add_field(LnmpField {
925 fid: 3,
926 value: LnmpValue::NestedRecord(Box::new(level2)),
927 });
928
929 let binary = encoder.encode_nested_record(&level1).unwrap();
930 let result = decoder.decode_nested_record(&binary);
931 assert!(result.is_ok());
932}
933
934#[test]
935fn test_decode_malformed_nested_structure_incomplete_field() {
936 let decoder = BinaryNestedDecoder::new();
937 let bytes = vec![0x06, 0x01, 0x01];
939
940 let result = decoder.decode_nested_record(&bytes);
941 assert!(matches!(result, Err(BinaryError::UnexpectedEof { .. })));
942}
943
944#[test]
945fn test_decode_malformed_nested_structure_invalid_fid() {
946 let decoder = BinaryNestedDecoder::new();
947 let bytes = vec![0x06, 0x01, 0x7F]; let result = decoder.decode_nested_record(&bytes);
951 assert!(matches!(result, Err(BinaryError::InvalidFID { .. })));
952}
953
954#[test]
955fn test_decode_nested_structure_not_allowed() {
956 let config = NestedDecoderConfig::new().with_allow_nested(false);
957 let decoder = BinaryNestedDecoder::with_config(config);
958
959 let bytes = vec![0x06, 0x00];
962
963 let result = decoder.decode_value_recursive(&bytes, 0);
964 assert!(matches!(
965 result,
966 Err(BinaryError::NestedStructureNotSupported)
967 ));
968}
969
970#[test]
971fn test_decode_nested_array_not_allowed() {
972 let config = NestedDecoderConfig::new().with_allow_nested(false);
973 let decoder = BinaryNestedDecoder::with_config(config);
974
975 let bytes = vec![0x07, 0x00];
978
979 let result = decoder.decode_value_recursive(&bytes, 0);
980 assert!(matches!(
981 result,
982 Err(BinaryError::NestedStructureNotSupported)
983 ));
984}
985
986#[test]
987fn test_decode_roundtrip_complex_nested_structure() {
988 use crate::binary::nested_encoder::BinaryNestedEncoder;
989
990 let encoder = BinaryNestedEncoder::new();
991 let decoder = BinaryNestedDecoder::new();
992
993 let mut inner1 = LnmpRecord::new();
995 inner1.add_field(LnmpField {
996 fid: 1,
997 value: LnmpValue::String("inner1".to_string()),
998 });
999
1000 let mut inner2 = LnmpRecord::new();
1001 inner2.add_field(LnmpField {
1002 fid: 2,
1003 value: LnmpValue::Int(42),
1004 });
1005
1006 let mut outer = LnmpRecord::new();
1007 outer.add_field(LnmpField {
1008 fid: 1,
1009 value: LnmpValue::NestedRecord(Box::new(inner1)),
1010 });
1011 outer.add_field(LnmpField {
1012 fid: 2,
1013 value: LnmpValue::NestedArray(vec![inner2]),
1014 });
1015 outer.add_field(LnmpField {
1016 fid: 3,
1017 value: LnmpValue::Bool(true),
1018 });
1019
1020 let binary = encoder.encode_nested_record(&outer).unwrap();
1021 let (decoded, _) = decoder.decode_nested_record(&binary).unwrap();
1022
1023 assert_eq!(decoded.sorted_fields().len(), outer.sorted_fields().len());
1024}
1025
1026#[test]
1027fn test_decode_empty_nested_records_at_multiple_levels() {
1028 use crate::binary::nested_encoder::BinaryNestedEncoder;
1029
1030 let encoder = BinaryNestedEncoder::new();
1031 let decoder = BinaryNestedDecoder::new();
1032
1033 let inner = LnmpRecord::new(); let mut outer = LnmpRecord::new();
1035 outer.add_field(LnmpField {
1036 fid: 1,
1037 value: LnmpValue::NestedRecord(Box::new(inner)),
1038 });
1039
1040 let binary = encoder.encode_nested_record(&outer).unwrap();
1041 let (decoded, _) = decoder.decode_nested_record(&binary).unwrap();
1042
1043 match &decoded.get_field(1).unwrap().value {
1044 LnmpValue::NestedRecord(inner) => {
1045 assert_eq!(inner.fields().len(), 0);
1046 }
1047 _ => panic!("Expected NestedRecord"),
1048 }
1049}
1050
1051#[test]
1052fn test_decode_empty_nested_arrays() {
1053 use crate::binary::nested_encoder::BinaryNestedEncoder;
1054
1055 let encoder = BinaryNestedEncoder::new();
1056 let decoder = BinaryNestedDecoder::new();
1057
1058 let mut record = LnmpRecord::new();
1059 record.add_field(LnmpField {
1060 fid: 1,
1061 value: LnmpValue::NestedArray(vec![]),
1062 });
1063
1064 let binary = encoder.encode_nested_record(&record).unwrap();
1065 let (decoded, _) = decoder.decode_nested_record(&binary).unwrap();
1066
1067 match &decoded.get_field(1).unwrap().value {
1068 LnmpValue::NestedArray(arr) => {
1069 assert_eq!(arr.len(), 0);
1070 }
1071 _ => panic!("Expected NestedArray"),
1072 }
1073}
1074
1075#[test]
1076fn test_decode_mixed_primitive_and_nested_fields() {
1077 use crate::binary::nested_encoder::BinaryNestedEncoder;
1078
1079 let encoder = BinaryNestedEncoder::new();
1080 let decoder = BinaryNestedDecoder::new();
1081
1082 let mut inner = LnmpRecord::new();
1083 inner.add_field(LnmpField {
1084 fid: 1,
1085 value: LnmpValue::String("inner".to_string()),
1086 });
1087
1088 let mut outer = LnmpRecord::new();
1089 outer.add_field(LnmpField {
1090 fid: 1,
1091 value: LnmpValue::Int(42),
1092 });
1093 outer.add_field(LnmpField {
1094 fid: 2,
1095 value: LnmpValue::NestedRecord(Box::new(inner)),
1096 });
1097 outer.add_field(LnmpField {
1098 fid: 3,
1099 value: LnmpValue::Bool(true),
1100 });
1101
1102 let binary = encoder.encode_nested_record(&outer).unwrap();
1103 let (decoded, _) = decoder.decode_nested_record(&binary).unwrap();
1104
1105 assert_eq!(decoded.fields().len(), 3);
1106 assert_eq!(decoded.get_field(1).unwrap().value, LnmpValue::Int(42));
1107 assert_eq!(decoded.get_field(3).unwrap().value, LnmpValue::Bool(true));
1108}
1109
1110#[test]
1111fn test_decode_depth_limit_enforced_at_exact_limit() {
1112 use crate::binary::nested_encoder::BinaryNestedEncoder;
1113
1114 let config = NestedDecoderConfig::new().with_max_depth(2);
1117 let decoder = BinaryNestedDecoder::with_config(config);
1118 let encoder = BinaryNestedEncoder::new();
1119
1120 let mut flat = LnmpRecord::new();
1122 flat.add_field(LnmpField {
1123 fid: 1,
1124 value: LnmpValue::Int(42),
1125 });
1126
1127 let binary = encoder.encode_nested_record(&flat).unwrap();
1128 let result = decoder.decode_nested_record(&binary);
1129 assert!(result.is_ok(), "Flat record should succeed");
1130
1131 let mut inner = LnmpRecord::new();
1133 inner.add_field(LnmpField {
1134 fid: 1,
1135 value: LnmpValue::Int(42),
1136 });
1137
1138 let mut outer = LnmpRecord::new();
1139 outer.add_field(LnmpField {
1140 fid: 2,
1141 value: LnmpValue::NestedRecord(Box::new(inner)),
1142 });
1143
1144 let binary = encoder.encode_nested_record(&outer).unwrap();
1145 let result = decoder.decode_nested_record(&binary);
1146 assert!(result.is_ok(), "One level of nesting should succeed");
1147
1148 let mut level3 = LnmpRecord::new();
1150 level3.add_field(LnmpField {
1151 fid: 1,
1152 value: LnmpValue::Int(42),
1153 });
1154
1155 let mut level2 = LnmpRecord::new();
1156 level2.add_field(LnmpField {
1157 fid: 2,
1158 value: LnmpValue::NestedRecord(Box::new(level3)),
1159 });
1160
1161 let mut level1 = LnmpRecord::new();
1162 level1.add_field(LnmpField {
1163 fid: 3,
1164 value: LnmpValue::NestedRecord(Box::new(level2)),
1165 });
1166
1167 let binary = encoder.encode_nested_record(&level1).unwrap();
1168 let result = decoder.decode_nested_record(&binary);
1169 assert!(
1170 matches!(result, Err(BinaryError::NestingDepthExceeded { .. })),
1171 "Two levels of nesting should fail with max_depth=2"
1172 );
1173}
1174
1175#[test]
1176fn test_decode_invalid_utf8_in_string() {
1177 let decoder = BinaryNestedDecoder::new();
1178 let bytes = vec![0x06, 0x01, 0x01, 0x04, 0x03, 0xFF, 0xFE, 0xFD];
1180
1181 let result = decoder.decode_nested_record(&bytes);
1182 assert!(matches!(result, Err(BinaryError::InvalidUtf8 { .. })));
1183}
1184
1185#[test]
1186fn test_decode_string_array_with_empty_strings() {
1187 use crate::binary::nested_encoder::BinaryNestedEncoder;
1188
1189 let encoder = BinaryNestedEncoder::new();
1190 let decoder = BinaryNestedDecoder::new();
1191
1192 let mut record = LnmpRecord::new();
1193 record.add_field(LnmpField {
1194 fid: 1,
1195 value: LnmpValue::StringArray(vec!["".to_string(), "test".to_string(), "".to_string()]),
1196 });
1197
1198 let binary = encoder.encode_nested_record(&record).unwrap();
1199 let (decoded, _) = decoder.decode_nested_record(&binary).unwrap();
1200
1201 assert_eq!(
1202 decoded.get_field(1).unwrap().value,
1203 LnmpValue::StringArray(vec!["".to_string(), "test".to_string(), "".to_string()])
1204 );
1205}