1use super::error::BinaryError;
8use super::types::TypeTag;
9use super::varint;
10use lnmp_core::{LnmpRecord, LnmpValue};
11
12#[derive(Debug, Clone)]
14pub struct NestedEncoderConfig {
15 pub max_depth: usize,
17 pub max_record_size: Option<usize>,
19 pub validate_canonical: bool,
21}
22
23impl Default for NestedEncoderConfig {
24 fn default() -> Self {
25 Self {
26 max_depth: 32,
27 max_record_size: None,
28 validate_canonical: false,
29 }
30 }
31}
32
33impl NestedEncoderConfig {
34 pub fn new() -> Self {
36 Self::default()
37 }
38
39 pub fn with_max_depth(mut self, max_depth: usize) -> Self {
41 self.max_depth = max_depth;
42 self
43 }
44
45 pub fn with_max_record_size(mut self, max_size: Option<usize>) -> Self {
47 self.max_record_size = max_size;
48 self
49 }
50
51 pub fn with_validate_canonical(mut self, validate: bool) -> Self {
53 self.validate_canonical = validate;
54 self
55 }
56}
57
58#[derive(Debug)]
62pub struct BinaryNestedEncoder {
63 config: NestedEncoderConfig,
64}
65
66impl BinaryNestedEncoder {
67 pub fn new() -> Self {
69 Self {
70 config: NestedEncoderConfig::default(),
71 }
72 }
73
74 pub fn with_config(config: NestedEncoderConfig) -> Self {
76 Self { config }
77 }
78
79 pub fn encode_nested_record(&self, record: &LnmpRecord) -> Result<Vec<u8>, BinaryError> {
104 self.encode_nested_record_with_depth(record, 0)
105 }
106
107 fn encode_nested_record_with_depth(
109 &self,
110 record: &LnmpRecord,
111 current_depth: usize,
112 ) -> Result<Vec<u8>, BinaryError> {
113 if current_depth >= self.config.max_depth {
115 return Err(BinaryError::NestingDepthExceeded {
116 depth: current_depth,
117 max: self.config.max_depth,
118 });
119 }
120
121 let mut buffer = Vec::new();
122
123 buffer.push(TypeTag::NestedRecord.to_u8());
125
126 let fields = record.sorted_fields();
128
129 let field_count = fields.len() as i64;
131 buffer.extend_from_slice(&varint::encode(field_count));
132
133 for field in fields {
135 buffer.extend_from_slice(&varint::encode(field.fid as i64));
137
138 let value_bytes = self.encode_value_recursive(&field.value, current_depth + 1)?;
140 buffer.extend_from_slice(&value_bytes);
141
142 if let Some(max_size) = self.config.max_record_size {
144 if buffer.len() > max_size {
145 return Err(BinaryError::RecordSizeExceeded {
146 size: buffer.len(),
147 max: max_size,
148 });
149 }
150 }
151 }
152
153 Ok(buffer)
154 }
155
156 pub fn encode_nested_array(&self, records: &[LnmpRecord]) -> Result<Vec<u8>, BinaryError> {
181 self.encode_nested_array_with_depth(records, 0)
182 }
183
184 fn encode_nested_array_with_depth(
186 &self,
187 records: &[LnmpRecord],
188 current_depth: usize,
189 ) -> Result<Vec<u8>, BinaryError> {
190 if current_depth >= self.config.max_depth {
192 return Err(BinaryError::NestingDepthExceeded {
193 depth: current_depth,
194 max: self.config.max_depth,
195 });
196 }
197
198 let mut buffer = Vec::new();
199
200 buffer.push(TypeTag::NestedArray.to_u8());
202
203 let element_count = records.len() as i64;
205 buffer.extend_from_slice(&varint::encode(element_count));
206
207 for record in records {
209 let record_bytes = self.encode_nested_record_with_depth(record, current_depth + 1)?;
211 buffer.extend_from_slice(&record_bytes);
212
213 if let Some(max_size) = self.config.max_record_size {
215 if buffer.len() > max_size {
216 return Err(BinaryError::RecordSizeExceeded {
217 size: buffer.len(),
218 max: max_size,
219 });
220 }
221 }
222 }
223
224 Ok(buffer)
225 }
226
227 fn encode_value_recursive(
229 &self,
230 value: &LnmpValue,
231 current_depth: usize,
232 ) -> Result<Vec<u8>, BinaryError> {
233 if current_depth >= self.config.max_depth
235 && matches!(
236 value,
237 LnmpValue::NestedRecord(_) | LnmpValue::NestedArray(_)
238 )
239 {
240 return Err(BinaryError::NestingDepthExceeded {
241 depth: current_depth,
242 max: self.config.max_depth,
243 });
244 }
245
246 match value {
247 LnmpValue::Int(i) => {
248 let mut buffer = Vec::new();
249 buffer.push(TypeTag::Int.to_u8());
250 buffer.extend_from_slice(&varint::encode(*i));
251 Ok(buffer)
252 }
253 LnmpValue::Float(f) => {
254 let mut buffer = Vec::new();
255 buffer.push(TypeTag::Float.to_u8());
256 buffer.extend_from_slice(&f.to_le_bytes());
257 Ok(buffer)
258 }
259 LnmpValue::Bool(b) => {
260 let mut buffer = Vec::new();
261 buffer.push(TypeTag::Bool.to_u8());
262 buffer.push(if *b { 0x01 } else { 0x00 });
263 Ok(buffer)
264 }
265 LnmpValue::String(s) => {
266 let mut buffer = Vec::new();
267 buffer.push(TypeTag::String.to_u8());
268 let bytes = s.as_bytes();
269 buffer.extend_from_slice(&varint::encode(bytes.len() as i64));
270 buffer.extend_from_slice(bytes);
271 Ok(buffer)
272 }
273 LnmpValue::StringArray(arr) => {
274 let mut buffer = Vec::new();
275 buffer.push(TypeTag::StringArray.to_u8());
276 buffer.extend_from_slice(&varint::encode(arr.len() as i64));
277 for s in arr {
278 let bytes = s.as_bytes();
279 buffer.extend_from_slice(&varint::encode(bytes.len() as i64));
280 buffer.extend_from_slice(bytes);
281 }
282 Ok(buffer)
283 }
284 LnmpValue::NestedRecord(record) => {
285 self.encode_nested_record_with_depth(record, current_depth)
286 }
287 LnmpValue::NestedArray(records) => {
288 self.encode_nested_array_with_depth(records, current_depth)
289 }
290 }
291 }
292}
293
294impl Default for BinaryNestedEncoder {
295 fn default() -> Self {
296 Self::new()
297 }
298}
299
300#[cfg(test)]
301mod tests {
302 #![allow(clippy::approx_constant)]
303
304 use super::*;
305 use lnmp_core::LnmpField;
306
307 #[test]
308 fn test_nested_encoder_config_default() {
309 let config = NestedEncoderConfig::default();
310 assert_eq!(config.max_depth, 32);
311 assert_eq!(config.max_record_size, None);
312 assert!(!config.validate_canonical);
313 }
314
315 #[test]
316 fn test_nested_encoder_config_builder() {
317 let config = NestedEncoderConfig::new()
318 .with_max_depth(16)
319 .with_max_record_size(Some(1024))
320 .with_validate_canonical(true);
321
322 assert_eq!(config.max_depth, 16);
323 assert_eq!(config.max_record_size, Some(1024));
324 assert!(config.validate_canonical);
325 }
326
327 #[test]
328 fn test_encode_empty_nested_record() {
329 let encoder = BinaryNestedEncoder::new();
330 let record = LnmpRecord::new();
331
332 let result = encoder.encode_nested_record(&record).unwrap();
333
334 assert_eq!(result[0], 0x06); assert_eq!(result[1], 0x00); assert_eq!(result.len(), 2);
338 }
339
340 #[test]
341 fn test_encode_single_level_nested_record() {
342 let encoder = BinaryNestedEncoder::new();
343 let mut record = LnmpRecord::new();
344 record.add_field(LnmpField {
345 fid: 1,
346 value: LnmpValue::Int(42),
347 });
348 record.add_field(LnmpField {
349 fid: 2,
350 value: LnmpValue::String("test".to_string()),
351 });
352
353 let result = encoder.encode_nested_record(&record).unwrap();
354
355 assert_eq!(result[0], 0x06); assert_eq!(result[1], 0x02); }
359
360 #[test]
361 fn test_encode_nested_record_canonical_ordering() {
362 let encoder = BinaryNestedEncoder::new();
363 let mut record = LnmpRecord::new();
364 record.add_field(LnmpField {
366 fid: 10,
367 value: LnmpValue::Int(3),
368 });
369 record.add_field(LnmpField {
370 fid: 2,
371 value: LnmpValue::Int(1),
372 });
373 record.add_field(LnmpField {
374 fid: 5,
375 value: LnmpValue::Int(2),
376 });
377
378 let result = encoder.encode_nested_record(&record).unwrap();
379
380 assert_eq!(result[0], 0x06); assert_eq!(result[1], 0x03); assert_eq!(result[2], 0x02); }
388
389 #[test]
390 fn test_encode_empty_nested_array() {
391 let encoder = BinaryNestedEncoder::new();
392 let records: Vec<LnmpRecord> = vec![];
393
394 let result = encoder.encode_nested_array(&records).unwrap();
395
396 assert_eq!(result[0], 0x07); assert_eq!(result[1], 0x00); assert_eq!(result.len(), 2);
400 }
401
402 #[test]
403 fn test_encode_nested_array_single_record() {
404 let encoder = BinaryNestedEncoder::new();
405 let mut record = LnmpRecord::new();
406 record.add_field(LnmpField {
407 fid: 1,
408 value: LnmpValue::Int(42),
409 });
410
411 let result = encoder.encode_nested_array(&[record]).unwrap();
412
413 assert_eq!(result[0], 0x07); assert_eq!(result[1], 0x01); assert_eq!(result[2], 0x06); }
419
420 #[test]
421 fn test_encode_nested_array_multiple_records() {
422 let encoder = BinaryNestedEncoder::new();
423 let mut record1 = LnmpRecord::new();
424 record1.add_field(LnmpField {
425 fid: 1,
426 value: LnmpValue::Int(1),
427 });
428
429 let mut record2 = LnmpRecord::new();
430 record2.add_field(LnmpField {
431 fid: 1,
432 value: LnmpValue::Int(2),
433 });
434
435 let result = encoder.encode_nested_array(&[record1, record2]).unwrap();
436
437 assert_eq!(result[0], 0x07); assert_eq!(result[1], 0x02); }
441
442 #[test]
443 fn test_encode_depth_limit_exceeded() {
444 let config = NestedEncoderConfig::new().with_max_depth(2);
445 let encoder = BinaryNestedEncoder::with_config(config);
446
447 let mut level3 = LnmpRecord::new();
449 level3.add_field(LnmpField {
450 fid: 1,
451 value: LnmpValue::Int(42),
452 });
453
454 let mut level2 = LnmpRecord::new();
455 level2.add_field(LnmpField {
456 fid: 2,
457 value: LnmpValue::NestedRecord(Box::new(level3)),
458 });
459
460 let mut level1 = LnmpRecord::new();
461 level1.add_field(LnmpField {
462 fid: 3,
463 value: LnmpValue::NestedRecord(Box::new(level2)),
464 });
465
466 let result = encoder.encode_nested_record(&level1);
467
468 assert!(result.is_err());
469 match result {
470 Err(BinaryError::NestingDepthExceeded { depth, max }) => {
471 assert_eq!(max, 2);
472 assert!(depth >= 2);
473 }
474 _ => panic!("Expected NestingDepthExceeded error"),
475 }
476 }
477
478 #[test]
479 fn test_encode_size_limit_exceeded() {
480 let config = NestedEncoderConfig::new().with_max_record_size(Some(10));
481 let encoder = BinaryNestedEncoder::with_config(config);
482
483 let mut record = LnmpRecord::new();
484 for i in 0..100 {
486 record.add_field(LnmpField {
487 fid: i,
488 value: LnmpValue::String("test".to_string()),
489 });
490 }
491
492 let result = encoder.encode_nested_record(&record);
493
494 assert!(result.is_err());
495 match result {
496 Err(BinaryError::RecordSizeExceeded { size, max }) => {
497 assert_eq!(max, 10);
498 assert!(size > 10);
499 }
500 _ => panic!("Expected RecordSizeExceeded error"),
501 }
502 }
503
504 #[test]
505 fn test_encode_multi_level_nested_record() {
506 let encoder = BinaryNestedEncoder::new();
507
508 let mut inner_record = LnmpRecord::new();
510 inner_record.add_field(LnmpField {
511 fid: 1,
512 value: LnmpValue::Int(42),
513 });
514
515 let mut outer_record = LnmpRecord::new();
516 outer_record.add_field(LnmpField {
517 fid: 2,
518 value: LnmpValue::NestedRecord(Box::new(inner_record)),
519 });
520
521 let result = encoder.encode_nested_record(&outer_record).unwrap();
522
523 assert_eq!(result[0], 0x06); assert_eq!(result[1], 0x01); assert_eq!(result[2], 0x02); assert_eq!(result[3], 0x06); }
531
532 #[test]
533 fn test_encode_nested_record_depth_2() {
534 let encoder = BinaryNestedEncoder::new();
535
536 let mut level2 = LnmpRecord::new();
538 level2.add_field(LnmpField {
539 fid: 1,
540 value: LnmpValue::Int(100),
541 });
542
543 let mut level1 = LnmpRecord::new();
544 level1.add_field(LnmpField {
545 fid: 2,
546 value: LnmpValue::NestedRecord(Box::new(level2)),
547 });
548
549 let result = encoder.encode_nested_record(&level1).unwrap();
550 assert!(!result.is_empty());
551 assert_eq!(result[0], 0x06); }
553
554 #[test]
555 fn test_encode_nested_record_depth_3() {
556 let encoder = BinaryNestedEncoder::new();
557
558 let mut level3 = LnmpRecord::new();
560 level3.add_field(LnmpField {
561 fid: 1,
562 value: LnmpValue::Int(100),
563 });
564
565 let mut level2 = LnmpRecord::new();
566 level2.add_field(LnmpField {
567 fid: 2,
568 value: LnmpValue::NestedRecord(Box::new(level3)),
569 });
570
571 let mut level1 = LnmpRecord::new();
572 level1.add_field(LnmpField {
573 fid: 3,
574 value: LnmpValue::NestedRecord(Box::new(level2)),
575 });
576
577 let result = encoder.encode_nested_record(&level1).unwrap();
578 assert!(!result.is_empty());
579 assert_eq!(result[0], 0x06); }
581
582 #[test]
583 fn test_encode_nested_record_depth_4() {
584 let encoder = BinaryNestedEncoder::new();
585
586 let mut level4 = LnmpRecord::new();
588 level4.add_field(LnmpField {
589 fid: 1,
590 value: LnmpValue::Int(100),
591 });
592
593 let mut level3 = LnmpRecord::new();
594 level3.add_field(LnmpField {
595 fid: 2,
596 value: LnmpValue::NestedRecord(Box::new(level4)),
597 });
598
599 let mut level2 = LnmpRecord::new();
600 level2.add_field(LnmpField {
601 fid: 3,
602 value: LnmpValue::NestedRecord(Box::new(level3)),
603 });
604
605 let mut level1 = LnmpRecord::new();
606 level1.add_field(LnmpField {
607 fid: 4,
608 value: LnmpValue::NestedRecord(Box::new(level2)),
609 });
610
611 let result = encoder.encode_nested_record(&level1).unwrap();
612 assert!(!result.is_empty());
613 assert_eq!(result[0], 0x06); }
615
616 #[test]
617 fn test_encode_nested_record_depth_5() {
618 let encoder = BinaryNestedEncoder::new();
619
620 let mut level5 = LnmpRecord::new();
622 level5.add_field(LnmpField {
623 fid: 1,
624 value: LnmpValue::Int(100),
625 });
626
627 let mut level4 = LnmpRecord::new();
628 level4.add_field(LnmpField {
629 fid: 2,
630 value: LnmpValue::NestedRecord(Box::new(level5)),
631 });
632
633 let mut level3 = LnmpRecord::new();
634 level3.add_field(LnmpField {
635 fid: 3,
636 value: LnmpValue::NestedRecord(Box::new(level4)),
637 });
638
639 let mut level2 = LnmpRecord::new();
640 level2.add_field(LnmpField {
641 fid: 4,
642 value: LnmpValue::NestedRecord(Box::new(level3)),
643 });
644
645 let mut level1 = LnmpRecord::new();
646 level1.add_field(LnmpField {
647 fid: 5,
648 value: LnmpValue::NestedRecord(Box::new(level2)),
649 });
650
651 let result = encoder.encode_nested_record(&level1).unwrap();
652 assert!(!result.is_empty());
653 assert_eq!(result[0], 0x06); }
655
656 #[test]
657 fn test_encode_depth_limit_enforced_at_exact_limit() {
658 let config = NestedEncoderConfig::new().with_max_depth(2);
659 let encoder = BinaryNestedEncoder::with_config(config);
660
661 let mut level1 = LnmpRecord::new();
663 level1.add_field(LnmpField {
664 fid: 1,
665 value: LnmpValue::Int(42),
666 });
667
668 let result = encoder.encode_nested_record(&level1);
670 assert!(result.is_ok());
671
672 let mut inner = LnmpRecord::new();
674 inner.add_field(LnmpField {
675 fid: 1,
676 value: LnmpValue::Int(42),
677 });
678
679 let mut outer = LnmpRecord::new();
680 outer.add_field(LnmpField {
681 fid: 2,
682 value: LnmpValue::NestedRecord(Box::new(inner)),
683 });
684
685 let result = encoder.encode_nested_record(&outer);
687 assert!(result.is_ok());
688
689 let mut level3 = LnmpRecord::new();
691 level3.add_field(LnmpField {
692 fid: 1,
693 value: LnmpValue::Int(42),
694 });
695
696 let mut level2 = LnmpRecord::new();
697 level2.add_field(LnmpField {
698 fid: 2,
699 value: LnmpValue::NestedRecord(Box::new(level3)),
700 });
701
702 let mut level1_deep = LnmpRecord::new();
703 level1_deep.add_field(LnmpField {
704 fid: 3,
705 value: LnmpValue::NestedRecord(Box::new(level2)),
706 });
707
708 let result = encoder.encode_nested_record(&level1_deep);
710 assert!(result.is_err());
711 }
712
713 #[test]
714 fn test_encode_size_limit_enforced_incrementally() {
715 let config = NestedEncoderConfig::new().with_max_record_size(Some(50));
716 let encoder = BinaryNestedEncoder::with_config(config);
717
718 let mut record = LnmpRecord::new();
719 for i in 0..20 {
721 record.add_field(LnmpField {
722 fid: i,
723 value: LnmpValue::String("test".to_string()),
724 });
725 }
726
727 let result = encoder.encode_nested_record(&record);
728 assert!(result.is_err());
729 match result {
730 Err(BinaryError::RecordSizeExceeded { .. }) => {}
731 _ => panic!("Expected RecordSizeExceeded error"),
732 }
733 }
734
735 #[test]
736 fn test_encode_canonical_ordering_at_all_levels() {
737 let encoder = BinaryNestedEncoder::new();
738
739 let mut inner = LnmpRecord::new();
741 inner.add_field(LnmpField {
742 fid: 10,
743 value: LnmpValue::Int(3),
744 });
745 inner.add_field(LnmpField {
746 fid: 2,
747 value: LnmpValue::Int(1),
748 });
749 inner.add_field(LnmpField {
750 fid: 5,
751 value: LnmpValue::Int(2),
752 });
753
754 let mut outer = LnmpRecord::new();
755 outer.add_field(LnmpField {
756 fid: 20,
757 value: LnmpValue::String("last".to_string()),
758 });
759 outer.add_field(LnmpField {
760 fid: 1,
761 value: LnmpValue::NestedRecord(Box::new(inner)),
762 });
763 outer.add_field(LnmpField {
764 fid: 15,
765 value: LnmpValue::String("middle".to_string()),
766 });
767
768 let result = encoder.encode_nested_record(&outer).unwrap();
769
770 assert_eq!(result[0], 0x06); assert_eq!(result[1], 0x03); assert_eq!(result[2], 0x01); }
775
776 #[test]
777 fn test_encode_empty_nested_records_at_multiple_levels() {
778 let encoder = BinaryNestedEncoder::new();
779
780 let inner = LnmpRecord::new(); let mut outer = LnmpRecord::new();
782 outer.add_field(LnmpField {
783 fid: 1,
784 value: LnmpValue::NestedRecord(Box::new(inner)),
785 });
786
787 let result = encoder.encode_nested_record(&outer).unwrap();
788
789 assert_eq!(result[0], 0x06); assert_eq!(result[1], 0x01); assert_eq!(result[2], 0x01); assert_eq!(result[3], 0x06); assert_eq!(result[4], 0x00); }
795
796 #[test]
797 fn test_encode_empty_nested_arrays() {
798 let encoder = BinaryNestedEncoder::new();
799
800 let mut record = LnmpRecord::new();
801 record.add_field(LnmpField {
802 fid: 1,
803 value: LnmpValue::NestedArray(vec![]),
804 });
805
806 let result = encoder.encode_nested_record(&record).unwrap();
807
808 assert_eq!(result[0], 0x06); assert_eq!(result[1], 0x01); assert_eq!(result[2], 0x01); assert_eq!(result[3], 0x07); assert_eq!(result[4], 0x00); }
814
815 #[test]
816 fn test_encode_nested_array_with_canonical_ordering() {
817 let encoder = BinaryNestedEncoder::new();
818
819 let mut record1 = LnmpRecord::new();
821 record1.add_field(LnmpField {
822 fid: 10,
823 value: LnmpValue::Int(2),
824 });
825 record1.add_field(LnmpField {
826 fid: 5,
827 value: LnmpValue::Int(1),
828 });
829
830 let mut record2 = LnmpRecord::new();
831 record2.add_field(LnmpField {
832 fid: 20,
833 value: LnmpValue::Int(4),
834 });
835 record2.add_field(LnmpField {
836 fid: 15,
837 value: LnmpValue::Int(3),
838 });
839
840 let result = encoder.encode_nested_array(&[record1, record2]).unwrap();
841
842 assert_eq!(result[0], 0x07); assert_eq!(result[1], 0x02); assert_eq!(result[2], 0x06); assert_eq!(result[3], 0x02); assert_eq!(result[4], 0x05); }
851
852 #[test]
853 fn test_encode_mixed_primitive_and_nested_fields() {
854 let encoder = BinaryNestedEncoder::new();
855
856 let mut inner = LnmpRecord::new();
857 inner.add_field(LnmpField {
858 fid: 1,
859 value: LnmpValue::String("inner".to_string()),
860 });
861
862 let mut outer = LnmpRecord::new();
863 outer.add_field(LnmpField {
864 fid: 1,
865 value: LnmpValue::Int(42),
866 });
867 outer.add_field(LnmpField {
868 fid: 2,
869 value: LnmpValue::NestedRecord(Box::new(inner)),
870 });
871 outer.add_field(LnmpField {
872 fid: 3,
873 value: LnmpValue::Bool(true),
874 });
875
876 let result = encoder.encode_nested_record(&outer).unwrap();
877
878 assert_eq!(result[0], 0x06); assert_eq!(result[1], 0x03); }
881
882 #[test]
883 fn test_encode_all_primitive_types_in_nested_record() {
884 let encoder = BinaryNestedEncoder::new();
885
886 let mut record = LnmpRecord::new();
887 record.add_field(LnmpField {
888 fid: 1,
889 value: LnmpValue::Int(-42),
890 });
891 record.add_field(LnmpField {
892 fid: 2,
893 value: LnmpValue::Float(3.14),
894 });
895 record.add_field(LnmpField {
896 fid: 3,
897 value: LnmpValue::Bool(false),
898 });
899 record.add_field(LnmpField {
900 fid: 4,
901 value: LnmpValue::String("test".to_string()),
902 });
903 record.add_field(LnmpField {
904 fid: 5,
905 value: LnmpValue::StringArray(vec!["a".to_string(), "b".to_string()]),
906 });
907
908 let result = encoder.encode_nested_record(&record).unwrap();
909
910 assert_eq!(result[0], 0x06); assert_eq!(result[1], 0x05); }
913}