lnmp_codec/binary/
types.rs

1//! Binary type system for LNMP v0.4 protocol.
2
3use super::error::BinaryError;
4use lnmp_core::LnmpValue;
5
6/// Type tag for binary values (LNMP v0.4/v0.5)
7#[repr(u8)]
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum TypeTag {
10    /// Integer type (VarInt encoded)
11    Int = 0x01,
12    /// Float type (8-byte IEEE754 LE)
13    Float = 0x02,
14    /// Boolean type (1 byte: 0x00 or 0x01)
15    Bool = 0x03,
16    /// String type (length VarInt + UTF-8 bytes)
17    String = 0x04,
18    /// String array type (count VarInt + repeated length+UTF-8)
19    StringArray = 0x05,
20    /// Nested record type (v0.5) - TAG + FIELD_COUNT + FID/VALUE pairs
21    NestedRecord = 0x06,
22    /// Nested array type (v0.5) - TAG + ELEMENT_COUNT + RECORD entries
23    NestedArray = 0x07,
24    /// Embedding type (v0.5) - TAG + ENCODED_VECTOR
25    Embedding = 0x08,
26    /// Reserved for future use (v0.5+)
27    Reserved09 = 0x09,
28    /// Quantized embedding type (v0.5.2) - TAG + QUANTIZED_VECTOR
29    QuantizedEmbedding = 0x0A,
30    /// Reserved for future use (v0.5+)
31    Reserved0B = 0x0B,
32    /// Reserved for future use (v0.5+)
33    Reserved0C = 0x0C,
34    /// Reserved for future use (v0.5+)
35    Reserved0D = 0x0D,
36    /// Reserved for future use (v0.5+)
37    Reserved0E = 0x0E,
38    /// Reserved for future use (v0.5+)
39    Reserved0F = 0x0F,
40}
41
42impl TypeTag {
43    /// Converts a byte to a TypeTag
44    pub fn from_u8(byte: u8) -> Result<Self, BinaryError> {
45        match byte {
46            0x01 => Ok(TypeTag::Int),
47            0x02 => Ok(TypeTag::Float),
48            0x03 => Ok(TypeTag::Bool),
49            0x04 => Ok(TypeTag::String),
50            0x05 => Ok(TypeTag::StringArray),
51            0x06 => Ok(TypeTag::NestedRecord),
52            0x07 => Ok(TypeTag::NestedArray),
53            0x08 => Ok(TypeTag::Embedding),
54            0x09 => Ok(TypeTag::Reserved09),
55            0x0A => Ok(TypeTag::QuantizedEmbedding),
56            0x0B => Ok(TypeTag::Reserved0B),
57            0x0C => Ok(TypeTag::Reserved0C),
58            0x0D => Ok(TypeTag::Reserved0D),
59            0x0E => Ok(TypeTag::Reserved0E),
60            0x0F => Ok(TypeTag::Reserved0F),
61            _ => Err(BinaryError::InvalidTypeTag { tag: byte }),
62        }
63    }
64
65    /// Converts the TypeTag to a byte
66    pub fn to_u8(self) -> u8 {
67        self as u8
68    }
69
70    /// Returns true if this is a v0.5+ type tag (nested or reserved)
71    pub fn is_v0_5_type(&self) -> bool {
72        matches!(
73            self,
74            TypeTag::NestedRecord
75                | TypeTag::NestedArray
76                | TypeTag::Embedding
77                | TypeTag::QuantizedEmbedding
78                | TypeTag::Reserved09
79                | TypeTag::Reserved0B
80                | TypeTag::Reserved0C
81                | TypeTag::Reserved0D
82                | TypeTag::Reserved0E
83                | TypeTag::Reserved0F
84        )
85    }
86
87    /// Returns true if this is a reserved type tag
88    pub fn is_reserved(&self) -> bool {
89        matches!(
90            self,
91            TypeTag::Reserved09
92                | TypeTag::Reserved0B
93                | TypeTag::Reserved0C
94                | TypeTag::Reserved0D
95                | TypeTag::Reserved0E
96                | TypeTag::Reserved0F
97        )
98    }
99}
100
101/// Binary value representation for LNMP v0.4/v0.5
102#[derive(Debug, Clone, PartialEq)]
103pub enum BinaryValue {
104    /// Integer value (i64)
105    Int(i64),
106    /// Floating-point value (f64)
107    Float(f64),
108    /// Boolean value
109    Bool(bool),
110    /// String value
111    String(String),
112    /// Array of strings
113    StringArray(Vec<String>),
114    /// Nested record (v0.5)
115    NestedRecord(Box<lnmp_core::LnmpRecord>),
116    /// Array of nested records (v0.5)
117    NestedArray(Vec<lnmp_core::LnmpRecord>),
118    /// Embedding (v0.5)
119    Embedding(lnmp_embedding::Vector),
120    /// Quantized embedding (v0.5.2)
121    QuantizedEmbedding(lnmp_quant::QuantizedVector),
122}
123
124impl BinaryValue {
125    /// Converts from LnmpValue to BinaryValue
126    ///
127    /// In v0.5, nested structures are supported. Use `from_lnmp_value_v0_4` for v0.4 compatibility.
128    pub fn from_lnmp_value(value: &LnmpValue) -> Result<Self, BinaryError> {
129        match value {
130            LnmpValue::Int(i) => Ok(BinaryValue::Int(*i)),
131            LnmpValue::Float(f) => Ok(BinaryValue::Float(*f)),
132            LnmpValue::Bool(b) => Ok(BinaryValue::Bool(*b)),
133            LnmpValue::String(s) => Ok(BinaryValue::String(s.clone())),
134            LnmpValue::StringArray(arr) => Ok(BinaryValue::StringArray(arr.clone())),
135            LnmpValue::NestedRecord(rec) => Ok(BinaryValue::NestedRecord(rec.clone())),
136            LnmpValue::NestedArray(arr) => Ok(BinaryValue::NestedArray(arr.clone())),
137            LnmpValue::Embedding(vec) => Ok(BinaryValue::Embedding(vec.clone())),
138            LnmpValue::EmbeddingDelta(_) => Err(BinaryError::InvalidValue {
139                reason: "EmbeddingDelta cannot be encoded as BinaryValue, use full embedding"
140                    .into(),
141                field_id: 0,
142                type_tag: 0x08,
143            }),
144            LnmpValue::QuantizedEmbedding(qv) => Ok(BinaryValue::QuantizedEmbedding(qv.clone())),
145        }
146    }
147
148    /// Converts from LnmpValue to BinaryValue (v0.4 compatibility mode)
149    ///
150    /// Returns an error if the value contains nested structures (not supported in v0.4)
151    pub fn from_lnmp_value_v0_4(value: &LnmpValue) -> Result<Self, BinaryError> {
152        match value {
153            LnmpValue::Int(i) => Ok(BinaryValue::Int(*i)),
154            LnmpValue::Float(f) => Ok(BinaryValue::Float(*f)),
155            LnmpValue::Bool(b) => Ok(BinaryValue::Bool(*b)),
156            LnmpValue::String(s) => Ok(BinaryValue::String(s.clone())),
157            LnmpValue::StringArray(arr) => Ok(BinaryValue::StringArray(arr.clone())),
158            LnmpValue::NestedRecord(_) => Err(BinaryError::InvalidValue {
159                field_id: 0,
160                type_tag: 0x06,
161                reason: "Nested records not supported in v0.4 binary format".to_string(),
162            }),
163            LnmpValue::NestedArray(_) => Err(BinaryError::InvalidValue {
164                field_id: 0,
165                type_tag: 0x07,
166                reason: "Nested arrays not supported in v0.4 binary format".to_string(),
167            }),
168            LnmpValue::Embedding(_) => Err(BinaryError::InvalidValue {
169                field_id: 0,
170                type_tag: 0x08,
171                reason: "Embeddings not supported in v0.4 binary format".to_string(),
172            }),
173            LnmpValue::EmbeddingDelta(_) => Err(BinaryError::InvalidValue {
174                reason: "EmbeddingDelta not supported in v0.4".to_string(),
175                field_id: 0,
176                type_tag: 0x08,
177            }),
178            LnmpValue::QuantizedEmbedding(_) => Err(BinaryError::InvalidValue {
179                reason: "QuantizedEmbedding not supported in v0.4".to_string(),
180                field_id: 0,
181                type_tag: 0x0A,
182            }),
183        }
184    }
185
186    /// Converts to LnmpValue
187    pub fn to_lnmp_value(&self) -> LnmpValue {
188        match self {
189            BinaryValue::Int(i) => LnmpValue::Int(*i),
190            BinaryValue::Float(f) => LnmpValue::Float(*f),
191            BinaryValue::Bool(b) => LnmpValue::Bool(*b),
192            BinaryValue::String(s) => LnmpValue::String(s.clone()),
193            BinaryValue::StringArray(arr) => LnmpValue::StringArray(arr.clone()),
194            BinaryValue::NestedRecord(rec) => LnmpValue::NestedRecord(rec.clone()),
195            BinaryValue::NestedArray(arr) => LnmpValue::NestedArray(arr.clone()),
196            BinaryValue::Embedding(vec) => LnmpValue::Embedding(vec.clone()),
197            BinaryValue::QuantizedEmbedding(qv) => LnmpValue::QuantizedEmbedding(qv.clone()),
198        }
199    }
200
201    /// Returns the type tag for this value
202    pub fn type_tag(&self) -> TypeTag {
203        match self {
204            BinaryValue::Int(_) => TypeTag::Int,
205            BinaryValue::Float(_) => TypeTag::Float,
206            BinaryValue::Bool(_) => TypeTag::Bool,
207            BinaryValue::String(_) => TypeTag::String,
208            BinaryValue::StringArray(_) => TypeTag::StringArray,
209            BinaryValue::NestedRecord(_) => TypeTag::NestedRecord,
210            BinaryValue::NestedArray(_) => TypeTag::NestedArray,
211            BinaryValue::Embedding(_) => TypeTag::Embedding,
212            BinaryValue::QuantizedEmbedding(_) => TypeTag::QuantizedEmbedding,
213        }
214    }
215}
216
217#[cfg(test)]
218mod tests {
219    #![allow(clippy::approx_constant)]
220
221    use super::*;
222    use lnmp_core::LnmpRecord;
223
224    #[test]
225    fn test_type_tag_from_u8() {
226        assert_eq!(TypeTag::from_u8(0x01).unwrap(), TypeTag::Int);
227        assert_eq!(TypeTag::from_u8(0x02).unwrap(), TypeTag::Float);
228        assert_eq!(TypeTag::from_u8(0x03).unwrap(), TypeTag::Bool);
229        assert_eq!(TypeTag::from_u8(0x04).unwrap(), TypeTag::String);
230        assert_eq!(TypeTag::from_u8(0x05).unwrap(), TypeTag::StringArray);
231    }
232
233    #[test]
234    fn test_type_tag_from_u8_invalid() {
235        assert!(TypeTag::from_u8(0x00).is_err());
236        assert!(TypeTag::from_u8(0xFF).is_err());
237    }
238
239    #[test]
240    fn test_type_tag_from_u8_v0_5_types() {
241        // v0.5 types should now be valid
242        assert_eq!(TypeTag::from_u8(0x06).unwrap(), TypeTag::NestedRecord);
243        assert_eq!(TypeTag::from_u8(0x07).unwrap(), TypeTag::NestedArray);
244    }
245
246    #[test]
247    fn test_type_tag_from_u8_reserved() {
248        // Reserved types should be valid but marked as reserved
249        assert_eq!(TypeTag::from_u8(0x08).unwrap(), TypeTag::Embedding);
250        assert_eq!(TypeTag::from_u8(0x09).unwrap(), TypeTag::Reserved09);
251        assert_eq!(TypeTag::from_u8(0x0A).unwrap(), TypeTag::QuantizedEmbedding);
252        assert_eq!(TypeTag::from_u8(0x0B).unwrap(), TypeTag::Reserved0B);
253        assert_eq!(TypeTag::from_u8(0x0C).unwrap(), TypeTag::Reserved0C);
254        assert_eq!(TypeTag::from_u8(0x0D).unwrap(), TypeTag::Reserved0D);
255        assert_eq!(TypeTag::from_u8(0x0E).unwrap(), TypeTag::Reserved0E);
256        assert_eq!(TypeTag::from_u8(0x0F).unwrap(), TypeTag::Reserved0F);
257    }
258
259    #[test]
260    fn test_type_tag_to_u8() {
261        assert_eq!(TypeTag::Int.to_u8(), 0x01);
262        assert_eq!(TypeTag::Float.to_u8(), 0x02);
263        assert_eq!(TypeTag::Bool.to_u8(), 0x03);
264        assert_eq!(TypeTag::String.to_u8(), 0x04);
265        assert_eq!(TypeTag::StringArray.to_u8(), 0x05);
266    }
267
268    #[test]
269    fn test_type_tag_round_trip() {
270        let tags = vec![
271            TypeTag::Int,
272            TypeTag::Float,
273            TypeTag::Bool,
274            TypeTag::String,
275            TypeTag::StringArray,
276            TypeTag::NestedRecord,
277            TypeTag::NestedArray,
278            TypeTag::Embedding,
279            TypeTag::Reserved09,
280            TypeTag::QuantizedEmbedding,
281            TypeTag::Reserved0B,
282            TypeTag::Reserved0C,
283            TypeTag::Reserved0D,
284            TypeTag::Reserved0E,
285            TypeTag::Reserved0F,
286        ];
287
288        for tag in tags {
289            let byte = tag.to_u8();
290            let parsed = TypeTag::from_u8(byte).unwrap();
291            assert_eq!(parsed, tag);
292        }
293    }
294
295    #[test]
296    fn test_type_tag_is_v0_5_type() {
297        // v0.4 types should return false
298        assert!(!TypeTag::Int.is_v0_5_type());
299        assert!(!TypeTag::Float.is_v0_5_type());
300        assert!(!TypeTag::Bool.is_v0_5_type());
301        assert!(!TypeTag::String.is_v0_5_type());
302        assert!(!TypeTag::StringArray.is_v0_5_type());
303
304        // v0.5 types should return true
305        assert!(TypeTag::NestedRecord.is_v0_5_type());
306        assert!(TypeTag::NestedArray.is_v0_5_type());
307        assert!(TypeTag::Embedding.is_v0_5_type());
308        assert!(TypeTag::Reserved09.is_v0_5_type());
309        assert!(TypeTag::QuantizedEmbedding.is_v0_5_type());
310        assert!(TypeTag::Reserved0B.is_v0_5_type());
311        assert!(TypeTag::Reserved0C.is_v0_5_type());
312        assert!(TypeTag::Reserved0D.is_v0_5_type());
313        assert!(TypeTag::Reserved0E.is_v0_5_type());
314        assert!(TypeTag::Reserved0F.is_v0_5_type());
315    }
316
317    #[test]
318    fn test_type_tag_is_reserved() {
319        // Non-reserved types should return false
320        assert!(!TypeTag::Int.is_reserved());
321        assert!(!TypeTag::Float.is_reserved());
322        assert!(!TypeTag::Bool.is_reserved());
323        assert!(!TypeTag::String.is_reserved());
324        assert!(!TypeTag::StringArray.is_reserved());
325        assert!(!TypeTag::NestedRecord.is_reserved());
326        assert!(!TypeTag::NestedArray.is_reserved());
327
328        // Reserved types should return true
329        assert!(!TypeTag::Embedding.is_reserved());
330        assert!(TypeTag::Reserved09.is_reserved());
331        assert!(!TypeTag::QuantizedEmbedding.is_reserved());
332        assert!(TypeTag::Reserved0B.is_reserved());
333        assert!(TypeTag::Reserved0C.is_reserved());
334        assert!(TypeTag::Reserved0D.is_reserved());
335        assert!(TypeTag::Reserved0E.is_reserved());
336        assert!(TypeTag::Reserved0F.is_reserved());
337    }
338
339    #[test]
340    fn test_binary_value_from_lnmp_int() {
341        let lnmp_val = LnmpValue::Int(42);
342        let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
343        assert_eq!(binary_val, BinaryValue::Int(42));
344    }
345
346    #[test]
347    fn test_binary_value_from_lnmp_float() {
348        let lnmp_val = LnmpValue::Float(3.14);
349        let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
350        assert_eq!(binary_val, BinaryValue::Float(3.14));
351    }
352
353    #[test]
354    fn test_binary_value_from_lnmp_bool() {
355        let lnmp_val = LnmpValue::Bool(true);
356        let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
357        assert_eq!(binary_val, BinaryValue::Bool(true));
358    }
359
360    #[test]
361    fn test_binary_value_from_lnmp_string() {
362        let lnmp_val = LnmpValue::String("hello".to_string());
363        let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
364        assert_eq!(binary_val, BinaryValue::String("hello".to_string()));
365    }
366
367    #[test]
368    fn test_binary_value_from_lnmp_string_array() {
369        let lnmp_val = LnmpValue::StringArray(vec!["a".to_string(), "b".to_string()]);
370        let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
371        assert_eq!(
372            binary_val,
373            BinaryValue::StringArray(vec!["a".to_string(), "b".to_string()])
374        );
375    }
376
377    #[test]
378    fn test_binary_value_from_lnmp_nested_record() {
379        // v0.5 now supports nested records
380        let nested = LnmpValue::NestedRecord(Box::new(LnmpRecord::new()));
381        let result = BinaryValue::from_lnmp_value(&nested);
382        assert!(result.is_ok());
383        match result.unwrap() {
384            BinaryValue::NestedRecord(_) => {}
385            _ => panic!("Expected NestedRecord variant"),
386        }
387    }
388
389    #[test]
390    fn test_binary_value_from_lnmp_nested_array() {
391        // v0.5 now supports nested arrays
392        let nested = LnmpValue::NestedArray(vec![]);
393        let result = BinaryValue::from_lnmp_value(&nested);
394        assert!(result.is_ok());
395        match result.unwrap() {
396            BinaryValue::NestedArray(_) => {}
397            _ => panic!("Expected NestedArray variant"),
398        }
399    }
400
401    #[test]
402    fn test_binary_value_from_lnmp_nested_record_error_v0_4() {
403        // v0.4 compatibility mode should still reject nested records
404        let nested = LnmpValue::NestedRecord(Box::new(LnmpRecord::new()));
405        let result = BinaryValue::from_lnmp_value_v0_4(&nested);
406        assert!(result.is_err());
407        match result {
408            Err(BinaryError::InvalidValue { reason, .. }) => {
409                assert!(reason.contains("not supported in v0.4"));
410            }
411            _ => panic!("Expected InvalidValue error"),
412        }
413    }
414
415    #[test]
416    fn test_binary_value_from_lnmp_nested_array_error_v0_4() {
417        // v0.4 compatibility mode should still reject nested arrays
418        let nested = LnmpValue::NestedArray(vec![]);
419        let result = BinaryValue::from_lnmp_value_v0_4(&nested);
420        assert!(result.is_err());
421        match result {
422            Err(BinaryError::InvalidValue { reason, .. }) => {
423                assert!(reason.contains("not supported in v0.4"));
424            }
425            _ => panic!("Expected InvalidValue error"),
426        }
427    }
428
429    #[test]
430    fn test_binary_value_to_lnmp_int() {
431        let binary_val = BinaryValue::Int(-42);
432        let lnmp_val = binary_val.to_lnmp_value();
433        assert_eq!(lnmp_val, LnmpValue::Int(-42));
434    }
435
436    #[test]
437    fn test_binary_value_to_lnmp_float() {
438        let binary_val = BinaryValue::Float(2.718);
439        let lnmp_val = binary_val.to_lnmp_value();
440        assert_eq!(lnmp_val, LnmpValue::Float(2.718));
441    }
442
443    #[test]
444    fn test_binary_value_to_lnmp_bool() {
445        let binary_val = BinaryValue::Bool(false);
446        let lnmp_val = binary_val.to_lnmp_value();
447        assert_eq!(lnmp_val, LnmpValue::Bool(false));
448    }
449
450    #[test]
451    fn test_binary_value_to_lnmp_string() {
452        let binary_val = BinaryValue::String("world".to_string());
453        let lnmp_val = binary_val.to_lnmp_value();
454        assert_eq!(lnmp_val, LnmpValue::String("world".to_string()));
455    }
456
457    #[test]
458    fn test_binary_value_to_lnmp_string_array() {
459        let binary_val = BinaryValue::StringArray(vec!["x".to_string(), "y".to_string()]);
460        let lnmp_val = binary_val.to_lnmp_value();
461        assert_eq!(
462            lnmp_val,
463            LnmpValue::StringArray(vec!["x".to_string(), "y".to_string()])
464        );
465    }
466
467    #[test]
468    fn test_binary_value_round_trip_int() {
469        let original = LnmpValue::Int(12345);
470        let binary = BinaryValue::from_lnmp_value(&original).unwrap();
471        let back = binary.to_lnmp_value();
472        assert_eq!(original, back);
473    }
474
475    #[test]
476    fn test_binary_value_round_trip_float() {
477        let original = LnmpValue::Float(1.414);
478        let binary = BinaryValue::from_lnmp_value(&original).unwrap();
479        let back = binary.to_lnmp_value();
480        assert_eq!(original, back);
481    }
482
483    #[test]
484    fn test_binary_value_round_trip_bool() {
485        let original = LnmpValue::Bool(true);
486        let binary = BinaryValue::from_lnmp_value(&original).unwrap();
487        let back = binary.to_lnmp_value();
488        assert_eq!(original, back);
489    }
490
491    #[test]
492    fn test_binary_value_round_trip_string() {
493        let original = LnmpValue::String("test".to_string());
494        let binary = BinaryValue::from_lnmp_value(&original).unwrap();
495        let back = binary.to_lnmp_value();
496        assert_eq!(original, back);
497    }
498
499    #[test]
500    fn test_binary_value_round_trip_string_array() {
501        let original = LnmpValue::StringArray(vec!["admin".to_string(), "dev".to_string()]);
502        let binary = BinaryValue::from_lnmp_value(&original).unwrap();
503        let back = binary.to_lnmp_value();
504        assert_eq!(original, back);
505    }
506
507    #[test]
508    fn test_binary_value_type_tag_int() {
509        let val = BinaryValue::Int(100);
510        assert_eq!(val.type_tag(), TypeTag::Int);
511    }
512
513    #[test]
514    fn test_binary_value_type_tag_float() {
515        let val = BinaryValue::Float(3.14);
516        assert_eq!(val.type_tag(), TypeTag::Float);
517    }
518
519    #[test]
520    fn test_binary_value_type_tag_bool() {
521        let val = BinaryValue::Bool(true);
522        assert_eq!(val.type_tag(), TypeTag::Bool);
523    }
524
525    #[test]
526    fn test_binary_value_type_tag_string() {
527        let val = BinaryValue::String("test".to_string());
528        assert_eq!(val.type_tag(), TypeTag::String);
529    }
530
531    #[test]
532    fn test_binary_value_type_tag_string_array() {
533        let val = BinaryValue::StringArray(vec!["a".to_string()]);
534        assert_eq!(val.type_tag(), TypeTag::StringArray);
535    }
536
537    #[test]
538    fn test_binary_value_type_tag_nested_record() {
539        let val = BinaryValue::NestedRecord(Box::new(LnmpRecord::new()));
540        assert_eq!(val.type_tag(), TypeTag::NestedRecord);
541    }
542
543    #[test]
544    fn test_binary_value_type_tag_nested_array() {
545        let val = BinaryValue::NestedArray(vec![]);
546        assert_eq!(val.type_tag(), TypeTag::NestedArray);
547    }
548
549    #[test]
550    fn test_binary_value_round_trip_nested_record() {
551        let mut record = LnmpRecord::new();
552        record.add_field(lnmp_core::LnmpField {
553            fid: 1,
554            value: LnmpValue::Int(42),
555        });
556        let original = LnmpValue::NestedRecord(Box::new(record));
557        let binary = BinaryValue::from_lnmp_value(&original).unwrap();
558        let back = binary.to_lnmp_value();
559        assert_eq!(original, back);
560    }
561
562    #[test]
563    fn test_binary_value_round_trip_nested_array() {
564        let mut record = LnmpRecord::new();
565        record.add_field(lnmp_core::LnmpField {
566            fid: 1,
567            value: LnmpValue::String("test".to_string()),
568        });
569        let original = LnmpValue::NestedArray(vec![record]);
570        let binary = BinaryValue::from_lnmp_value(&original).unwrap();
571        let back = binary.to_lnmp_value();
572        assert_eq!(original, back);
573    }
574
575    #[test]
576    fn test_binary_value_empty_string() {
577        let lnmp_val = LnmpValue::String("".to_string());
578        let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
579        assert_eq!(binary_val, BinaryValue::String("".to_string()));
580        let back = binary_val.to_lnmp_value();
581        assert_eq!(back, lnmp_val);
582    }
583
584    #[test]
585    fn test_binary_value_empty_string_array() {
586        let lnmp_val = LnmpValue::StringArray(vec![]);
587        let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
588        assert_eq!(binary_val, BinaryValue::StringArray(vec![]));
589        let back = binary_val.to_lnmp_value();
590        assert_eq!(back, lnmp_val);
591    }
592
593    #[test]
594    fn test_binary_value_negative_int() {
595        let lnmp_val = LnmpValue::Int(-9999);
596        let binary_val = BinaryValue::from_lnmp_value(&lnmp_val).unwrap();
597        assert_eq!(binary_val, BinaryValue::Int(-9999));
598        let back = binary_val.to_lnmp_value();
599        assert_eq!(back, lnmp_val);
600    }
601
602    #[test]
603    fn test_binary_value_special_floats() {
604        // Test NaN
605        let nan_val = LnmpValue::Float(f64::NAN);
606        let binary_nan = BinaryValue::from_lnmp_value(&nan_val).unwrap();
607        match binary_nan {
608            BinaryValue::Float(f) => assert!(f.is_nan()),
609            _ => panic!("Expected Float variant"),
610        }
611
612        // Test Infinity
613        let inf_val = LnmpValue::Float(f64::INFINITY);
614        let binary_inf = BinaryValue::from_lnmp_value(&inf_val).unwrap();
615        assert_eq!(binary_inf, BinaryValue::Float(f64::INFINITY));
616
617        // Test Negative Infinity
618        let neg_inf_val = LnmpValue::Float(f64::NEG_INFINITY);
619        let binary_neg_inf = BinaryValue::from_lnmp_value(&neg_inf_val).unwrap();
620        assert_eq!(binary_neg_inf, BinaryValue::Float(f64::NEG_INFINITY));
621    }
622}