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