1use super::delta::{DeltaConfig, DeltaEncoder};
7use super::error::BinaryError;
8use super::frame::BinaryFrame;
9use crate::config::{ParserConfig, ParsingMode, TextInputMode};
10use crate::parser::Parser;
11use lnmp_core::{LnmpField, LnmpRecord};
12
13#[derive(Debug, Clone)]
15pub struct EncoderConfig {
16 pub validate_canonical: bool,
18 pub sort_fields: bool,
20 pub text_input_mode: TextInputMode,
22 pub semantic_dictionary: Option<lnmp_sfe::SemanticDictionary>,
24
25 pub enable_nested_binary: bool,
28 pub max_depth: usize,
30 pub streaming_mode: bool,
32 pub delta_mode: bool,
34 pub chunk_size: usize,
36}
37
38impl Default for EncoderConfig {
39 fn default() -> Self {
40 Self {
41 validate_canonical: false,
42 sort_fields: true,
43 text_input_mode: TextInputMode::Strict,
44 semantic_dictionary: None,
45 enable_nested_binary: false,
47 max_depth: 32,
48 streaming_mode: false,
49 delta_mode: false,
50 chunk_size: 4096,
51 }
52 }
53}
54
55impl EncoderConfig {
56 pub fn new() -> Self {
58 Self::default()
59 }
60
61 pub fn with_validate_canonical(mut self, validate: bool) -> Self {
63 self.validate_canonical = validate;
64 self.sort_fields = true;
65 self
66 }
67
68 pub fn with_sort_fields(mut self, sort: bool) -> Self {
70 self.sort_fields = sort;
71 self
72 }
73
74 pub fn with_text_input_mode(mut self, mode: TextInputMode) -> Self {
76 self.text_input_mode = mode;
77 self
78 }
79
80 pub fn with_semantic_dictionary(mut self, dict: lnmp_sfe::SemanticDictionary) -> Self {
82 self.semantic_dictionary = Some(dict);
83 self
84 }
85
86 pub fn with_nested_binary(mut self, enable: bool) -> Self {
90 self.enable_nested_binary = enable;
91 self
92 }
93
94 pub fn with_max_depth(mut self, depth: usize) -> Self {
96 self.max_depth = depth;
97 self
98 }
99
100 pub fn with_streaming_mode(mut self, enable: bool) -> Self {
102 self.streaming_mode = enable;
103 self
104 }
105
106 pub fn with_delta_mode(mut self, enable: bool) -> Self {
108 self.delta_mode = enable;
109 self
110 }
111
112 pub fn with_chunk_size(mut self, size: usize) -> Self {
114 self.chunk_size = size;
115 self
116 }
117
118 pub fn with_v0_4_compatibility(mut self) -> Self {
134 self.enable_nested_binary = false;
135 self.streaming_mode = false;
136 self.delta_mode = false;
137 self
138 }
139}
140
141#[derive(Debug)]
166pub struct BinaryEncoder {
167 config: EncoderConfig,
168 normalizer: Option<crate::normalizer::ValueNormalizer>,
169}
170
171impl BinaryEncoder {
172 pub fn new() -> Self {
178 Self {
179 config: EncoderConfig::default(),
180 normalizer: None,
181 }
182 }
183
184 pub fn with_config(config: EncoderConfig) -> Self {
186 let normalizer = config.semantic_dictionary.as_ref().map(|dict| {
187 crate::normalizer::ValueNormalizer::new(crate::normalizer::NormalizationConfig {
188 semantic_dictionary: Some(dict.clone()),
189 ..crate::normalizer::NormalizationConfig::default()
190 })
191 });
192 Self { config, normalizer }
193 }
194
195 pub fn with_delta_mode(mut self, enable: bool) -> Self {
197 self.config.delta_mode = enable;
198 self
199 }
200
201 pub fn encode(&self, record: &LnmpRecord) -> Result<Vec<u8>, BinaryError> {
222 if self.config.streaming_mode {
224 return Err(BinaryError::UnsupportedFeature {
225 feature: "binary streaming mode".to_string(),
226 });
227 }
228 if self.config.enable_nested_binary {
229 return Err(BinaryError::UnsupportedFeature {
230 feature: "nested binary encoding".to_string(),
231 });
232 }
233 if self.config.chunk_size == 0 {
234 return Err(BinaryError::UnsupportedFeature {
235 feature: "chunk_size=0 is invalid".to_string(),
236 });
237 }
238
239 if !self.config.enable_nested_binary {
241 self.validate_v0_4_compatibility(record)?;
243 }
244
245 let normalized_record = if let Some(norm) = &self.normalizer {
247 let mut out = LnmpRecord::new();
248 for field in record.fields() {
249 let normalized_value = norm.normalize_with_fid(Some(field.fid), &field.value);
250 out.add_field(LnmpField {
251 fid: field.fid,
252 value: normalized_value,
253 });
254 }
255 out
256 } else {
257 record.clone()
258 };
259
260 let frame = BinaryFrame::from_record(&normalized_record)?;
262
263 Ok(frame.encode())
265 }
266
267 fn validate_v0_4_compatibility(&self, record: &LnmpRecord) -> Result<(), BinaryError> {
276 use lnmp_core::LnmpValue;
277
278 for field in record.fields() {
279 match &field.value {
280 LnmpValue::NestedRecord(_) => {
281 return Err(BinaryError::InvalidValue {
282 field_id: field.fid,
283 type_tag: 0x06,
284 reason: "Nested records not supported in v0.4 binary format. Use v0.5 with enable_nested_binary=true or convert to flat structure.".to_string(),
285 });
286 }
287 LnmpValue::NestedArray(_) => {
288 return Err(BinaryError::InvalidValue {
289 field_id: field.fid,
290 type_tag: 0x07,
291 reason: "Nested arrays not supported in v0.4 binary format. Use v0.5 with enable_nested_binary=true or convert to flat structure.".to_string(),
292 });
293 }
294 _ => {} }
296 }
297
298 Ok(())
299 }
300
301 pub fn encode_text(&self, text: &str) -> Result<Vec<u8>, BinaryError> {
333 self.encode_text_with_mode(text, self.config.text_input_mode)
334 }
335
336 pub fn encode_text_strict(&self, text: &str) -> Result<Vec<u8>, BinaryError> {
338 self.encode_text_with_mode(text, TextInputMode::Strict)
339 }
340
341 pub fn encode_text_lenient(&self, text: &str) -> Result<Vec<u8>, BinaryError> {
343 self.encode_text_with_mode(text, TextInputMode::Lenient)
344 }
345
346 pub fn encode_text_strict_profile(&self, text: &str) -> Result<Vec<u8>, BinaryError> {
348 self.encode_text_with_profile(text, TextInputMode::Strict, ParsingMode::Strict)
349 }
350
351 pub fn encode_text_llm_profile(&self, text: &str) -> Result<Vec<u8>, BinaryError> {
353 self.encode_text_with_profile(text, TextInputMode::Lenient, ParsingMode::Loose)
354 }
355
356 pub fn encode_text_with_profile(
361 &self,
362 text: &str,
363 text_mode: TextInputMode,
364 parsing_mode: ParsingMode,
365 ) -> Result<Vec<u8>, BinaryError> {
366 self.encode_text_internal(text, text_mode, parsing_mode)
367 }
368
369 fn encode_text_with_mode(
370 &self,
371 text: &str,
372 mode: TextInputMode,
373 ) -> Result<Vec<u8>, BinaryError> {
374 self.encode_text_internal(text, mode, ParserConfig::default().mode)
375 }
376
377 fn encode_text_internal(
378 &self,
379 text: &str,
380 text_mode: TextInputMode,
381 parsing_mode: ParsingMode,
382 ) -> Result<Vec<u8>, BinaryError> {
383 let parser_config = ParserConfig {
384 mode: parsing_mode,
385 text_input_mode: text_mode,
386 ..ParserConfig::default()
387 };
388
389 let mut parser = Parser::with_config(text, parser_config)
390 .map_err(|e| BinaryError::TextFormatError { source: e })?;
391 let record = parser
392 .parse_record()
393 .map_err(|e| BinaryError::TextFormatError { source: e })?;
394 self.encode(&record)
395 }
396
397 pub fn encode_delta_from(
403 &self,
404 base: &LnmpRecord,
405 updated: &LnmpRecord,
406 delta_config: Option<DeltaConfig>,
407 ) -> Result<Vec<u8>, BinaryError> {
408 let mut config = delta_config.unwrap_or_default();
410 if self.config.delta_mode {
412 config.enable_delta = true;
413 }
414
415 if !self.config.delta_mode && !config.enable_delta {
417 return Err(BinaryError::DeltaError {
418 reason: "Delta mode not enabled in encoder or provided delta config".to_string(),
419 });
420 }
421
422 let delta_encoder = DeltaEncoder::with_config(config);
425 let compute_result = delta_encoder.compute_delta(base, updated);
426 match compute_result {
427 Ok(ops) => match delta_encoder.encode_delta(&ops) {
428 Ok(bytes) => Ok(bytes),
429 Err(e) => Err(BinaryError::DeltaError {
430 reason: format!("encode_delta failed: {}", e),
431 }),
432 },
433 Err(e) => Err(BinaryError::DeltaError {
434 reason: format!("compute_delta failed: {}", e),
435 }),
436 }
437 }
438}
439
440impl Default for BinaryEncoder {
441 fn default() -> Self {
442 Self::new()
443 }
444}
445
446#[cfg(test)]
447mod tests {
448 #![allow(clippy::approx_constant)]
449
450 use super::*;
451 use crate::binary::BinaryDecoder;
452 use lnmp_core::{LnmpField, LnmpValue};
453
454 #[test]
455 fn test_new_encoder() {
456 let encoder = BinaryEncoder::new();
457 assert!(encoder.config.sort_fields);
458 assert!(!encoder.config.validate_canonical);
459 assert_eq!(encoder.config.text_input_mode, TextInputMode::Strict);
461 assert!(!encoder.config.enable_nested_binary);
462 assert_eq!(encoder.config.max_depth, 32);
463 assert!(!encoder.config.streaming_mode);
464 assert!(!encoder.config.delta_mode);
465 assert_eq!(encoder.config.chunk_size, 4096);
466 }
467
468 #[test]
469 fn test_encoder_with_config() {
470 let config = EncoderConfig::new()
471 .with_validate_canonical(true)
472 .with_sort_fields(false);
473
474 let encoder = BinaryEncoder::with_config(config);
475 assert!(!encoder.config.sort_fields);
476 assert!(encoder.config.validate_canonical);
477 }
478
479 #[test]
480 fn test_encode_empty_record() {
481 let record = LnmpRecord::new();
482 let encoder = BinaryEncoder::new();
483 let binary = encoder.encode(&record).unwrap();
484
485 assert_eq!(binary.len(), 3);
487 assert_eq!(binary[0], 0x04); assert_eq!(binary[1], 0x00); assert_eq!(binary[2], 0x00); }
491
492 #[test]
493 fn test_encode_single_field() {
494 let mut record = LnmpRecord::new();
495 record.add_field(LnmpField {
496 fid: 7,
497 value: LnmpValue::Bool(true),
498 });
499
500 let encoder = BinaryEncoder::new();
501 let binary = encoder.encode(&record).unwrap();
502
503 assert!(binary.len() > 3);
505 assert_eq!(binary[0], 0x04); assert_eq!(binary[1], 0x00); assert_eq!(binary[2], 0x01); }
509
510 #[test]
511 fn test_encode_multiple_fields() {
512 let mut record = LnmpRecord::new();
513 record.add_field(LnmpField {
514 fid: 7,
515 value: LnmpValue::Bool(true),
516 });
517 record.add_field(LnmpField {
518 fid: 12,
519 value: LnmpValue::Int(14532),
520 });
521 record.add_field(LnmpField {
522 fid: 23,
523 value: LnmpValue::StringArray(vec!["admin".to_string(), "dev".to_string()]),
524 });
525
526 let encoder = BinaryEncoder::new();
527 let binary = encoder.encode(&record).unwrap();
528
529 assert_eq!(binary[0], 0x04); assert_eq!(binary[1], 0x00); assert_eq!(binary[2], 0x03); }
533
534 #[test]
535 fn test_encode_sorts_fields() {
536 let mut record = LnmpRecord::new();
537 record.add_field(LnmpField {
539 fid: 23,
540 value: LnmpValue::StringArray(vec!["admin".to_string()]),
541 });
542 record.add_field(LnmpField {
543 fid: 7,
544 value: LnmpValue::Bool(true),
545 });
546 record.add_field(LnmpField {
547 fid: 12,
548 value: LnmpValue::Int(14532),
549 });
550
551 let encoder = BinaryEncoder::new();
552 let binary = encoder.encode(&record).unwrap();
553
554 use super::super::frame::BinaryFrame;
556 let frame = BinaryFrame::decode(&binary).unwrap();
557 let decoded_record = frame.to_record();
558
559 let fields = decoded_record.fields();
561 assert_eq!(fields[0].fid, 7);
562 assert_eq!(fields[1].fid, 12);
563 assert_eq!(fields[2].fid, 23);
564 }
565
566 #[test]
567 fn test_encode_delta_integration() {
568 use crate::binary::{DeltaConfig, DeltaDecoder};
569 use lnmp_core::{LnmpField, LnmpValue};
570
571 let mut base = LnmpRecord::new();
572 base.add_field(LnmpField {
573 fid: 1,
574 value: LnmpValue::Int(1),
575 });
576 base.add_field(LnmpField {
577 fid: 2,
578 value: LnmpValue::String("v1".to_string()),
579 });
580
581 let mut updated = base.clone();
582 updated.remove_field(1);
583 updated.add_field(LnmpField {
584 fid: 1,
585 value: LnmpValue::Int(2),
586 });
587
588 let encoder = BinaryEncoder::new();
590 let err = encoder
591 .encode_delta_from(&base, &updated, None)
592 .unwrap_err();
593 assert!(matches!(err, BinaryError::DeltaError { .. }));
594
595 let config = DeltaConfig::new().with_enable_delta(true);
597 let bytes = encoder
598 .encode_delta_from(&base, &updated, Some(config))
599 .unwrap();
600 assert_eq!(bytes[0], crate::binary::DELTA_TAG);
601
602 let delta_decoder = DeltaDecoder::with_config(DeltaConfig::new().with_enable_delta(true));
604 let ops = delta_decoder.decode_delta(&bytes).unwrap();
605 let mut result = base.clone();
606 delta_decoder.apply_delta(&mut result, &ops).unwrap();
607
608 assert_eq!(result.fields(), updated.fields());
610 }
611
612 #[test]
613 fn test_encode_delta_from_encoder_config_enabled() {
614 use crate::binary::EncoderConfig;
615 use lnmp_core::{LnmpField, LnmpValue};
616
617 let mut base = LnmpRecord::new();
618 base.add_field(LnmpField {
619 fid: 1,
620 value: LnmpValue::Int(1),
621 });
622 base.add_field(LnmpField {
623 fid: 2,
624 value: LnmpValue::String("v1".to_string()),
625 });
626
627 let mut updated = base.clone();
628 updated.remove_field(1);
629 updated.add_field(LnmpField {
630 fid: 1,
631 value: LnmpValue::Int(2),
632 });
633
634 let config = EncoderConfig::new().with_delta_mode(true);
636 let encoder = BinaryEncoder::with_config(config);
637 let bytes = encoder.encode_delta_from(&base, &updated, None).unwrap();
638 assert_eq!(bytes[0], crate::binary::DELTA_TAG);
639 }
640
641 #[test]
642 fn test_encode_text_simple() {
643 let text = "F7=1";
644 let encoder = BinaryEncoder::new();
645 let binary = encoder.encode_text(text).unwrap();
646
647 assert_eq!(binary[0], 0x04); assert_eq!(binary[1], 0x00); assert_eq!(binary[2], 0x01); }
651
652 #[test]
653 fn test_encode_text_multiple_fields() {
654 let text = "F7=1;F12=14532;F23=[\"admin\",\"dev\"]";
655 let encoder = BinaryEncoder::new();
656 let binary = encoder.encode_text(text).unwrap();
657
658 assert_eq!(binary[0], 0x04); assert_eq!(binary[1], 0x00); assert_eq!(binary[2], 0x03); }
662
663 #[test]
664 fn test_encode_text_unsorted() {
665 let text = "F23=[\"admin\"];F7=1;F12=14532";
666 let encoder = BinaryEncoder::new();
667 let binary = encoder.encode_text(text).unwrap();
668
669 use super::super::frame::BinaryFrame;
671 let frame = BinaryFrame::decode(&binary).unwrap();
672 let decoded_record = frame.to_record();
673
674 let fields = decoded_record.fields();
675 assert_eq!(fields[0].fid, 7);
676 assert_eq!(fields[1].fid, 12);
677 assert_eq!(fields[2].fid, 23);
678 }
679
680 #[test]
681 fn test_encode_text_with_newlines() {
682 let text = "F7=1\nF12=14532\nF23=[\"admin\",\"dev\"]";
683 let encoder = BinaryEncoder::new();
684 let binary = encoder.encode_text(text).unwrap();
685
686 assert_eq!(binary[0], 0x04); assert_eq!(binary[1], 0x00); assert_eq!(binary[2], 0x03); }
690
691 #[test]
692 fn test_encode_text_lenient_repairs_quotes() {
693 let text = "F7=\"hello";
694 let encoder = BinaryEncoder::with_config(
695 EncoderConfig::new().with_text_input_mode(TextInputMode::Lenient),
696 );
697 let binary = encoder.encode_text(text).unwrap();
698
699 assert_eq!(binary[0], 0x04); }
701
702 #[test]
703 fn test_encode_text_all_types() {
704 let text = "F1=-42;F2=3.14;F3=0;F4=\"hello\";F5=[\"a\",\"b\"]";
705 let encoder = BinaryEncoder::new();
706 let binary = encoder.encode_text(text).unwrap();
707
708 use super::super::frame::BinaryFrame;
710 let frame = BinaryFrame::decode(&binary).unwrap();
711 let decoded_record = frame.to_record();
712
713 assert_eq!(
714 decoded_record.get_field(1).unwrap().value,
715 LnmpValue::Int(-42)
716 );
717 assert_eq!(
718 decoded_record.get_field(2).unwrap().value,
719 LnmpValue::Float(3.14)
720 );
721 assert_eq!(
722 decoded_record.get_field(3).unwrap().value,
723 LnmpValue::Bool(false)
724 );
725 assert_eq!(
726 decoded_record.get_field(4).unwrap().value,
727 LnmpValue::String("hello".to_string())
728 );
729 assert_eq!(
730 decoded_record.get_field(5).unwrap().value,
731 LnmpValue::StringArray(vec!["a".to_string(), "b".to_string()])
732 );
733 }
734
735 #[test]
736 fn test_encode_text_strict_profile_rejects_loose_input() {
737 let text = "F7=1;F12=14532; # comment should fail strict grammar";
738 let encoder = BinaryEncoder::new();
739 let err = encoder.encode_text_strict_profile(text).unwrap_err();
740 match err {
741 BinaryError::TextFormatError { .. } => {}
742 other => panic!("expected TextFormatError, got {:?}", other),
743 }
744 }
745
746 #[test]
747 fn test_encode_text_llm_profile_lenient_succeeds() {
748 let text = "F7=\"hello;world\";F8=unquoted token";
749 let encoder = BinaryEncoder::new();
750 let binary = encoder.encode_text_llm_profile(text).unwrap();
751 assert_eq!(binary[0], 0x04);
752 }
753
754 #[test]
755 fn test_encode_text_invalid() {
756 let text = "INVALID";
757 let encoder = BinaryEncoder::new();
758 let result = encoder.encode_text(text);
759
760 assert!(result.is_err());
761 }
762
763 #[test]
764 fn test_encode_text_empty() {
765 let text = "";
766 let encoder = BinaryEncoder::new();
767 let binary = encoder.encode_text(text).unwrap();
768
769 assert_eq!(binary.len(), 3);
771 assert_eq!(binary[0], 0x04); assert_eq!(binary[1], 0x00); assert_eq!(binary[2], 0x00); }
775
776 #[test]
777 fn test_encode_all_value_types() {
778 let mut record = LnmpRecord::new();
779 record.add_field(LnmpField {
780 fid: 1,
781 value: LnmpValue::Int(42),
782 });
783 record.add_field(LnmpField {
784 fid: 2,
785 value: LnmpValue::Float(2.718),
786 });
787 record.add_field(LnmpField {
788 fid: 3,
789 value: LnmpValue::Bool(false),
790 });
791 record.add_field(LnmpField {
792 fid: 4,
793 value: LnmpValue::String("world".to_string()),
794 });
795 record.add_field(LnmpField {
796 fid: 5,
797 value: LnmpValue::StringArray(vec!["x".to_string(), "y".to_string()]),
798 });
799
800 let encoder = BinaryEncoder::new();
801 let binary = encoder.encode(&record).unwrap();
802
803 use super::super::frame::BinaryFrame;
805 let frame = BinaryFrame::decode(&binary).unwrap();
806 let decoded_record = frame.to_record();
807
808 assert_eq!(decoded_record.fields().len(), 5);
809 assert_eq!(
810 decoded_record.get_field(1).unwrap().value,
811 LnmpValue::Int(42)
812 );
813 assert_eq!(
814 decoded_record.get_field(2).unwrap().value,
815 LnmpValue::Float(2.718)
816 );
817 assert_eq!(
818 decoded_record.get_field(3).unwrap().value,
819 LnmpValue::Bool(false)
820 );
821 assert_eq!(
822 decoded_record.get_field(4).unwrap().value,
823 LnmpValue::String("world".to_string())
824 );
825 assert_eq!(
826 decoded_record.get_field(5).unwrap().value,
827 LnmpValue::StringArray(vec!["x".to_string(), "y".to_string()])
828 );
829 }
830
831 #[test]
832 fn test_default_encoder() {
833 let encoder = BinaryEncoder::default();
834 assert!(encoder.config.sort_fields);
835 assert!(!encoder.config.validate_canonical);
836 }
837
838 #[test]
839 fn test_encoder_config_builder() {
840 let config = EncoderConfig::new()
841 .with_validate_canonical(true)
842 .with_sort_fields(true);
843
844 assert!(config.validate_canonical);
845 assert!(config.sort_fields);
846 }
847
848 #[test]
849 fn test_encoder_config_v05_fields() {
850 let config = EncoderConfig::new()
851 .with_nested_binary(true)
852 .with_max_depth(64)
853 .with_streaming_mode(true)
854 .with_delta_mode(true)
855 .with_chunk_size(8192);
856
857 assert!(config.enable_nested_binary);
858 assert_eq!(config.max_depth, 64);
859 assert!(config.streaming_mode);
860 assert!(config.delta_mode);
861 assert_eq!(config.chunk_size, 8192);
862 }
863
864 #[test]
865 fn test_encoder_config_v05_defaults() {
866 let config = EncoderConfig::default();
867
868 assert!(!config.enable_nested_binary);
869 assert_eq!(config.max_depth, 32);
870 assert!(!config.streaming_mode);
871 assert!(!config.delta_mode);
872 assert_eq!(config.chunk_size, 4096);
873 }
874
875 #[test]
876 fn test_encoder_config_backward_compatibility() {
877 let v04_config = EncoderConfig::new()
879 .with_validate_canonical(true)
880 .with_sort_fields(true);
881
882 assert!(v04_config.validate_canonical);
884 assert!(v04_config.sort_fields);
885
886 assert!(!v04_config.enable_nested_binary);
888 assert!(!v04_config.streaming_mode);
889 assert!(!v04_config.delta_mode);
890 }
891
892 #[test]
893 fn test_encoder_config_mixed_v04_v05() {
894 let config = EncoderConfig::new()
896 .with_validate_canonical(true) .with_nested_binary(true) .with_sort_fields(true) .with_streaming_mode(true); assert!(config.validate_canonical);
902 assert!(config.sort_fields);
903 assert!(config.enable_nested_binary);
904 assert!(config.streaming_mode);
905 }
906
907 #[test]
908 fn test_encoder_applies_semantic_dictionary() {
909 let mut record = LnmpRecord::new();
910 record.add_field(LnmpField {
911 fid: 23,
912 value: LnmpValue::StringArray(vec!["Admin".to_string()]),
913 });
914
915 let mut dict = lnmp_sfe::SemanticDictionary::new();
916 dict.add_equivalence(23, "Admin".to_string(), "admin".to_string());
917
918 let config = EncoderConfig::new()
919 .with_semantic_dictionary(dict)
920 .with_validate_canonical(true);
921 let encoder = BinaryEncoder::with_config(config);
922
923 let binary = encoder.encode(&record).unwrap();
924 let decoder = BinaryDecoder::new();
925 let decoded = decoder.decode(&binary).unwrap();
926
927 match decoded.get_field(23).unwrap().value.clone() {
928 LnmpValue::StringArray(vals) => assert_eq!(vals, vec!["admin".to_string()]),
929 other => panic!("unexpected value {:?}", other),
930 }
931 }
932
933 #[test]
934 fn test_encoder_rejects_streaming_mode_until_implemented() {
935 let config = EncoderConfig::new().with_streaming_mode(true);
936 let encoder = BinaryEncoder::with_config(config);
937 let mut record = LnmpRecord::new();
938 record.add_field(LnmpField {
939 fid: 1,
940 value: LnmpValue::Int(1),
941 });
942 let err = encoder.encode(&record).unwrap_err();
943 assert!(matches!(err, BinaryError::UnsupportedFeature { .. }));
944 }
945
946 #[test]
947 fn test_encoder_rejects_nested_binary_flag_until_implemented() {
948 let config = EncoderConfig::new().with_nested_binary(true);
949 let encoder = BinaryEncoder::with_config(config);
950 let mut record = LnmpRecord::new();
951 record.add_field(LnmpField {
952 fid: 1,
953 value: LnmpValue::Int(1),
954 });
955 let err = encoder.encode(&record).unwrap_err();
956 assert!(matches!(err, BinaryError::UnsupportedFeature { .. }));
957 }
958
959 #[test]
960 fn test_encoder_rejects_zero_chunk_size() {
961 let config = EncoderConfig::new().with_chunk_size(0);
962 let encoder = BinaryEncoder::with_config(config);
963 let mut record = LnmpRecord::new();
964 record.add_field(LnmpField {
965 fid: 1,
966 value: LnmpValue::Int(1),
967 });
968 let err = encoder.encode(&record).unwrap_err();
969 assert!(matches!(err, BinaryError::UnsupportedFeature { .. }));
970 }
971
972 #[test]
973 fn test_encoder_v04_mode_encoding() {
974 let config = EncoderConfig::new()
976 .with_nested_binary(false)
977 .with_streaming_mode(false)
978 .with_delta_mode(false);
979
980 let encoder = BinaryEncoder::with_config(config);
981
982 let mut record = LnmpRecord::new();
984 record.add_field(LnmpField {
985 fid: 7,
986 value: LnmpValue::Bool(true),
987 });
988
989 let binary = encoder.encode(&record).unwrap();
990
991 assert_eq!(binary[0], 0x04); assert_eq!(binary[1], 0x00); assert_eq!(binary[2], 0x01); }
996}