lnmp_codec/binary/
entry.rs

1//! Binary entry structure for LNMP v0.4 protocol.
2//!
3//! A BinaryEntry represents a single field in binary format, consisting of:
4//! - FID (Field Identifier): 2 bytes, little-endian
5//! - TAG (Type Tag): 1 byte
6//! - VALUE: Variable length, encoding depends on type
7
8use super::error::BinaryError;
9use super::types::{BinaryValue, TypeTag};
10use super::varint;
11use lnmp_core::{FieldId, LnmpField};
12
13/// A single field encoded in binary format
14#[derive(Debug, Clone, PartialEq)]
15pub struct BinaryEntry {
16    /// Field identifier (16-bit unsigned integer)
17    pub fid: FieldId,
18    /// Type tag indicating value type
19    pub tag: TypeTag,
20    /// The encoded value
21    pub value: BinaryValue,
22}
23
24impl BinaryEntry {
25    /// Creates a new binary entry
26    pub fn new(fid: FieldId, value: BinaryValue) -> Self {
27        Self {
28            fid,
29            tag: value.type_tag(),
30            value,
31        }
32    }
33
34    /// Creates a new binary entry from an LnmpField
35    ///
36    /// # Errors
37    ///
38    /// Returns `BinaryError::InvalidValue` if the field contains nested structures
39    /// (not supported in v0.4 binary format)
40    pub fn from_field(field: &LnmpField) -> Result<Self, BinaryError> {
41        let value = BinaryValue::from_lnmp_value(&field.value).map_err(|e| match e {
42            BinaryError::InvalidValue {
43                type_tag, reason, ..
44            } => BinaryError::InvalidValue {
45                field_id: field.fid,
46                type_tag,
47                reason,
48            },
49            other => other,
50        })?;
51
52        Ok(Self {
53            fid: field.fid,
54            tag: value.type_tag(),
55            value,
56        })
57    }
58
59    /// Converts to an LnmpField
60    pub fn to_field(&self) -> LnmpField {
61        LnmpField {
62            fid: self.fid,
63            value: self.value.to_lnmp_value(),
64        }
65    }
66
67    /// Returns the type tag of this entry
68    pub fn type_tag(&self) -> TypeTag {
69        self.tag
70    }
71
72    /// Encodes the entry to bytes
73    ///
74    /// Binary layout:
75    /// ```text
76    /// ┌──────────┬──────────┬──────────────────┐
77    /// │   FID    │  THTAG   │      VALUE       │
78    /// │ (2 bytes)│ (1 byte) │   (variable)     │
79    /// └──────────┴──────────┴──────────────────┘
80    /// ```
81    pub fn encode(&self) -> Vec<u8> {
82        let mut bytes = Vec::new();
83
84        // Write FID (2 bytes, little-endian)
85        bytes.extend_from_slice(&self.fid.to_le_bytes());
86
87        // Write TAG (1 byte)
88        bytes.push(self.tag.to_u8());
89
90        // Write VALUE (encoding depends on type)
91        match &self.value {
92            BinaryValue::Int(i) => {
93                // VarInt encoding
94                bytes.extend_from_slice(&varint::encode(*i));
95            }
96            BinaryValue::Float(f) => {
97                // 8 bytes IEEE754 little-endian
98                bytes.extend_from_slice(&f.to_le_bytes());
99            }
100            BinaryValue::Bool(b) => {
101                // 1 byte: 0x00 for false, 0x01 for true
102                bytes.push(if *b { 0x01 } else { 0x00 });
103            }
104            BinaryValue::String(s) => {
105                // Length (VarInt) + UTF-8 bytes
106                let utf8_bytes = s.as_bytes();
107                bytes.extend_from_slice(&varint::encode(utf8_bytes.len() as i64));
108                bytes.extend_from_slice(utf8_bytes);
109            }
110            BinaryValue::StringArray(arr) => {
111                // Count (VarInt) + repeated (length + UTF-8 bytes)
112                bytes.extend_from_slice(&varint::encode(arr.len() as i64));
113                for s in arr {
114                    let utf8_bytes = s.as_bytes();
115                    bytes.extend_from_slice(&varint::encode(utf8_bytes.len() as i64));
116                    bytes.extend_from_slice(utf8_bytes);
117                }
118            }
119            BinaryValue::NestedRecord(_) | BinaryValue::NestedArray(_) => {
120                // Nested structure encoding will be implemented in task 2
121                // For now, this is a placeholder that should not be reached
122                // as nested encoding requires special handling
123                panic!("Nested structure encoding not yet implemented - use BinaryNestedEncoder");
124            }
125        }
126
127        bytes
128    }
129
130    /// Decodes an entry from bytes
131    ///
132    /// Returns a tuple of (BinaryEntry, bytes_consumed)
133    ///
134    /// # Errors
135    ///
136    /// Returns errors for:
137    /// - `UnexpectedEof`: Insufficient data
138    /// - `InvalidTypeTag`: Unknown type tag
139    /// - `InvalidVarInt`: Malformed VarInt
140    /// - `InvalidUtf8`: Invalid UTF-8 in string
141    /// - `InvalidValue`: Other value decoding errors
142    pub fn decode(bytes: &[u8]) -> Result<(Self, usize), BinaryError> {
143        let mut offset = 0;
144
145        // Read FID (2 bytes, little-endian)
146        if bytes.len() < 2 {
147            return Err(BinaryError::UnexpectedEof {
148                expected: 2,
149                found: bytes.len(),
150            });
151        }
152        let fid = u16::from_le_bytes([bytes[0], bytes[1]]);
153        offset += 2;
154
155        // Read TAG (1 byte)
156        if bytes.len() < offset + 1 {
157            return Err(BinaryError::UnexpectedEof {
158                expected: offset + 1,
159                found: bytes.len(),
160            });
161        }
162        let tag = TypeTag::from_u8(bytes[offset])?;
163        offset += 1;
164
165        // Read VALUE (depends on type)
166        let value = match tag {
167            TypeTag::NestedRecord | TypeTag::NestedArray => {
168                // Nested structure decoding will be implemented in task 3
169                return Err(BinaryError::InvalidValue {
170                    field_id: fid,
171                    type_tag: tag.to_u8(),
172                    reason:
173                        "Nested structure decoding not yet implemented - use BinaryNestedDecoder"
174                            .to_string(),
175                });
176            }
177            TypeTag::Reserved08
178            | TypeTag::Reserved09
179            | TypeTag::Reserved0A
180            | TypeTag::Reserved0B
181            | TypeTag::Reserved0C
182            | TypeTag::Reserved0D
183            | TypeTag::Reserved0E
184            | TypeTag::Reserved0F => {
185                return Err(BinaryError::InvalidValue {
186                    field_id: fid,
187                    type_tag: tag.to_u8(),
188                    reason: format!("Reserved type tag 0x{:02X} cannot be used", tag.to_u8()),
189                });
190            }
191            TypeTag::Int => {
192                let (int_val, consumed) =
193                    varint::decode(&bytes[offset..]).map_err(|_| BinaryError::InvalidValue {
194                        field_id: fid,
195                        type_tag: tag.to_u8(),
196                        reason: "Invalid VarInt encoding".to_string(),
197                    })?;
198                offset += consumed;
199                BinaryValue::Int(int_val)
200            }
201            TypeTag::Float => {
202                if bytes.len() < offset + 8 {
203                    return Err(BinaryError::UnexpectedEof {
204                        expected: offset + 8,
205                        found: bytes.len(),
206                    });
207                }
208                let float_bytes: [u8; 8] = bytes[offset..offset + 8]
209                    .try_into()
210                    .expect("slice length checked");
211                let float_val = f64::from_le_bytes(float_bytes);
212                offset += 8;
213                BinaryValue::Float(float_val)
214            }
215            TypeTag::Bool => {
216                if bytes.len() < offset + 1 {
217                    return Err(BinaryError::UnexpectedEof {
218                        expected: offset + 1,
219                        found: bytes.len(),
220                    });
221                }
222                let bool_val = match bytes[offset] {
223                    0x00 => false,
224                    0x01 => true,
225                    other => {
226                        return Err(BinaryError::InvalidValue {
227                            field_id: fid,
228                            type_tag: tag.to_u8(),
229                            reason: format!(
230                                "Invalid boolean value: 0x{:02X} (expected 0x00 or 0x01)",
231                                other
232                            ),
233                        });
234                    }
235                };
236                offset += 1;
237                BinaryValue::Bool(bool_val)
238            }
239            TypeTag::String => {
240                let (length, consumed) =
241                    varint::decode(&bytes[offset..]).map_err(|_| BinaryError::InvalidValue {
242                        field_id: fid,
243                        type_tag: tag.to_u8(),
244                        reason: "Invalid string length VarInt".to_string(),
245                    })?;
246                offset += consumed;
247
248                if length < 0 {
249                    return Err(BinaryError::InvalidValue {
250                        field_id: fid,
251                        type_tag: tag.to_u8(),
252                        reason: format!("Negative string length: {}", length),
253                    });
254                }
255
256                let length = length as usize;
257                if bytes.len() < offset + length {
258                    return Err(BinaryError::UnexpectedEof {
259                        expected: offset + length,
260                        found: bytes.len(),
261                    });
262                }
263
264                let string_val = std::str::from_utf8(&bytes[offset..offset + length])
265                    .map_err(|_| BinaryError::InvalidUtf8 { field_id: fid })?
266                    .to_string();
267                offset += length;
268                BinaryValue::String(string_val)
269            }
270            TypeTag::StringArray => {
271                let (count, consumed) =
272                    varint::decode(&bytes[offset..]).map_err(|_| BinaryError::InvalidValue {
273                        field_id: fid,
274                        type_tag: tag.to_u8(),
275                        reason: "Invalid array count VarInt".to_string(),
276                    })?;
277                offset += consumed;
278
279                if count < 0 {
280                    return Err(BinaryError::InvalidValue {
281                        field_id: fid,
282                        type_tag: tag.to_u8(),
283                        reason: format!("Negative array count: {}", count),
284                    });
285                }
286
287                let count = count as usize;
288                let mut strings = Vec::with_capacity(count);
289
290                for _ in 0..count {
291                    let (length, consumed) = varint::decode(&bytes[offset..]).map_err(|_| {
292                        BinaryError::InvalidValue {
293                            field_id: fid,
294                            type_tag: tag.to_u8(),
295                            reason: "Invalid string length in array".to_string(),
296                        }
297                    })?;
298                    offset += consumed;
299
300                    if length < 0 {
301                        return Err(BinaryError::InvalidValue {
302                            field_id: fid,
303                            type_tag: tag.to_u8(),
304                            reason: format!("Negative string length in array: {}", length),
305                        });
306                    }
307
308                    let length = length as usize;
309                    if bytes.len() < offset + length {
310                        return Err(BinaryError::UnexpectedEof {
311                            expected: offset + length,
312                            found: bytes.len(),
313                        });
314                    }
315
316                    let string_val = std::str::from_utf8(&bytes[offset..offset + length])
317                        .map_err(|_| BinaryError::InvalidUtf8 { field_id: fid })?
318                        .to_string();
319                    offset += length;
320                    strings.push(string_val);
321                }
322
323                BinaryValue::StringArray(strings)
324            }
325        };
326
327        Ok((Self { fid, tag, value }, offset))
328    }
329}
330
331#[cfg(test)]
332mod tests {
333    #![allow(clippy::approx_constant)]
334
335    use super::*;
336    use lnmp_core::LnmpValue;
337
338    #[test]
339    fn test_from_field_int() {
340        let field = LnmpField {
341            fid: 12,
342            value: LnmpValue::Int(14532),
343        };
344
345        let entry = BinaryEntry::from_field(&field).unwrap();
346        assert_eq!(entry.fid, 12);
347        assert_eq!(entry.tag, TypeTag::Int);
348        assert_eq!(entry.value, BinaryValue::Int(14532));
349    }
350
351    #[test]
352    fn test_from_field_float() {
353        let field = LnmpField {
354            fid: 5,
355            value: LnmpValue::Float(3.14),
356        };
357
358        let entry = BinaryEntry::from_field(&field).unwrap();
359        assert_eq!(entry.fid, 5);
360        assert_eq!(entry.tag, TypeTag::Float);
361        assert_eq!(entry.value, BinaryValue::Float(3.14));
362    }
363
364    #[test]
365    fn test_from_field_bool() {
366        let field = LnmpField {
367            fid: 7,
368            value: LnmpValue::Bool(true),
369        };
370
371        let entry = BinaryEntry::from_field(&field).unwrap();
372        assert_eq!(entry.fid, 7);
373        assert_eq!(entry.tag, TypeTag::Bool);
374        assert_eq!(entry.value, BinaryValue::Bool(true));
375    }
376
377    #[test]
378    fn test_from_field_string() {
379        let field = LnmpField {
380            fid: 1,
381            value: LnmpValue::String("hello".to_string()),
382        };
383
384        let entry = BinaryEntry::from_field(&field).unwrap();
385        assert_eq!(entry.fid, 1);
386        assert_eq!(entry.tag, TypeTag::String);
387        assert_eq!(entry.value, BinaryValue::String("hello".to_string()));
388    }
389
390    #[test]
391    fn test_from_field_string_array() {
392        let field = LnmpField {
393            fid: 23,
394            value: LnmpValue::StringArray(vec!["admin".to_string(), "dev".to_string()]),
395        };
396
397        let entry = BinaryEntry::from_field(&field).unwrap();
398        assert_eq!(entry.fid, 23);
399        assert_eq!(entry.tag, TypeTag::StringArray);
400        assert_eq!(
401            entry.value,
402            BinaryValue::StringArray(vec!["admin".to_string(), "dev".to_string()])
403        );
404    }
405
406    #[test]
407    fn test_to_field_int() {
408        let entry = BinaryEntry {
409            fid: 12,
410            tag: TypeTag::Int,
411            value: BinaryValue::Int(42),
412        };
413
414        let field = entry.to_field();
415        assert_eq!(field.fid, 12);
416        assert_eq!(field.value, LnmpValue::Int(42));
417    }
418
419    #[test]
420    fn test_to_field_float() {
421        let entry = BinaryEntry {
422            fid: 5,
423            tag: TypeTag::Float,
424            value: BinaryValue::Float(2.718),
425        };
426
427        let field = entry.to_field();
428        assert_eq!(field.fid, 5);
429        assert_eq!(field.value, LnmpValue::Float(2.718));
430    }
431
432    #[test]
433    fn test_to_field_bool() {
434        let entry = BinaryEntry {
435            fid: 7,
436            tag: TypeTag::Bool,
437            value: BinaryValue::Bool(false),
438        };
439
440        let field = entry.to_field();
441        assert_eq!(field.fid, 7);
442        assert_eq!(field.value, LnmpValue::Bool(false));
443    }
444
445    #[test]
446    fn test_to_field_string() {
447        let entry = BinaryEntry {
448            fid: 1,
449            tag: TypeTag::String,
450            value: BinaryValue::String("world".to_string()),
451        };
452
453        let field = entry.to_field();
454        assert_eq!(field.fid, 1);
455        assert_eq!(field.value, LnmpValue::String("world".to_string()));
456    }
457
458    #[test]
459    fn test_to_field_string_array() {
460        let entry = BinaryEntry {
461            fid: 23,
462            tag: TypeTag::StringArray,
463            value: BinaryValue::StringArray(vec!["x".to_string(), "y".to_string()]),
464        };
465
466        let field = entry.to_field();
467        assert_eq!(field.fid, 23);
468        assert_eq!(
469            field.value,
470            LnmpValue::StringArray(vec!["x".to_string(), "y".to_string()])
471        );
472    }
473
474    #[test]
475    fn test_encode_int() {
476        let entry = BinaryEntry {
477            fid: 12,
478            tag: TypeTag::Int,
479            value: BinaryValue::Int(14532),
480        };
481
482        let bytes = entry.encode();
483
484        // FID (12 in little-endian)
485        assert_eq!(bytes[0], 0x0C);
486        assert_eq!(bytes[1], 0x00);
487        // TAG (Int = 0x01)
488        assert_eq!(bytes[2], 0x01);
489        // VALUE (14532 as VarInt)
490        let varint_bytes = varint::encode(14532);
491        assert_eq!(&bytes[3..], &varint_bytes[..]);
492    }
493
494    #[test]
495    fn test_encode_float() {
496        let entry = BinaryEntry {
497            fid: 5,
498            tag: TypeTag::Float,
499            value: BinaryValue::Float(3.14),
500        };
501
502        let bytes = entry.encode();
503
504        // FID (5 in little-endian)
505        assert_eq!(bytes[0], 0x05);
506        assert_eq!(bytes[1], 0x00);
507        // TAG (Float = 0x02)
508        assert_eq!(bytes[2], 0x02);
509        // VALUE (3.14 as IEEE754 LE)
510        let float_bytes = 3.14f64.to_le_bytes();
511        assert_eq!(&bytes[3..11], &float_bytes[..]);
512    }
513
514    #[test]
515    fn test_encode_bool_true() {
516        let entry = BinaryEntry {
517            fid: 7,
518            tag: TypeTag::Bool,
519            value: BinaryValue::Bool(true),
520        };
521
522        let bytes = entry.encode();
523
524        // FID (7 in little-endian)
525        assert_eq!(bytes[0], 0x07);
526        assert_eq!(bytes[1], 0x00);
527        // TAG (Bool = 0x03)
528        assert_eq!(bytes[2], 0x03);
529        // VALUE (true = 0x01)
530        assert_eq!(bytes[3], 0x01);
531    }
532
533    #[test]
534    fn test_encode_bool_false() {
535        let entry = BinaryEntry {
536            fid: 7,
537            tag: TypeTag::Bool,
538            value: BinaryValue::Bool(false),
539        };
540
541        let bytes = entry.encode();
542
543        // FID (7 in little-endian)
544        assert_eq!(bytes[0], 0x07);
545        assert_eq!(bytes[1], 0x00);
546        // TAG (Bool = 0x03)
547        assert_eq!(bytes[2], 0x03);
548        // VALUE (false = 0x00)
549        assert_eq!(bytes[3], 0x00);
550    }
551
552    #[test]
553    fn test_encode_string() {
554        let entry = BinaryEntry {
555            fid: 1,
556            tag: TypeTag::String,
557            value: BinaryValue::String("hello".to_string()),
558        };
559
560        let bytes = entry.encode();
561
562        // FID (1 in little-endian)
563        assert_eq!(bytes[0], 0x01);
564        assert_eq!(bytes[1], 0x00);
565        // TAG (String = 0x04)
566        assert_eq!(bytes[2], 0x04);
567        // VALUE (length VarInt + UTF-8)
568        let length_varint = varint::encode(5);
569        assert_eq!(&bytes[3..3 + length_varint.len()], &length_varint[..]);
570        let offset = 3 + length_varint.len();
571        assert_eq!(&bytes[offset..], b"hello");
572    }
573
574    #[test]
575    fn test_encode_string_array() {
576        let entry = BinaryEntry {
577            fid: 23,
578            tag: TypeTag::StringArray,
579            value: BinaryValue::StringArray(vec!["admin".to_string(), "dev".to_string()]),
580        };
581
582        let bytes = entry.encode();
583
584        // FID (23 in little-endian)
585        assert_eq!(bytes[0], 0x17);
586        assert_eq!(bytes[1], 0x00);
587        // TAG (StringArray = 0x05)
588        assert_eq!(bytes[2], 0x05);
589
590        let mut offset = 3;
591        // Count VarInt (2)
592        let count_varint = varint::encode(2);
593        assert_eq!(
594            &bytes[offset..offset + count_varint.len()],
595            &count_varint[..]
596        );
597        offset += count_varint.len();
598
599        // First string "admin"
600        let len1_varint = varint::encode(5);
601        assert_eq!(&bytes[offset..offset + len1_varint.len()], &len1_varint[..]);
602        offset += len1_varint.len();
603        assert_eq!(&bytes[offset..offset + 5], b"admin");
604        offset += 5;
605
606        // Second string "dev"
607        let len2_varint = varint::encode(3);
608        assert_eq!(&bytes[offset..offset + len2_varint.len()], &len2_varint[..]);
609        offset += len2_varint.len();
610        assert_eq!(&bytes[offset..offset + 3], b"dev");
611    }
612
613    #[test]
614    fn test_decode_int() {
615        let entry = BinaryEntry {
616            fid: 12,
617            tag: TypeTag::Int,
618            value: BinaryValue::Int(14532),
619        };
620
621        let bytes = entry.encode();
622        let (decoded, consumed) = BinaryEntry::decode(&bytes).unwrap();
623
624        assert_eq!(decoded, entry);
625        assert_eq!(consumed, bytes.len());
626    }
627
628    #[test]
629    fn test_decode_float() {
630        let entry = BinaryEntry {
631            fid: 5,
632            tag: TypeTag::Float,
633            value: BinaryValue::Float(3.14),
634        };
635
636        let bytes = entry.encode();
637        let (decoded, consumed) = BinaryEntry::decode(&bytes).unwrap();
638
639        assert_eq!(decoded, entry);
640        assert_eq!(consumed, bytes.len());
641    }
642
643    #[test]
644    fn test_decode_bool() {
645        let entry = BinaryEntry {
646            fid: 7,
647            tag: TypeTag::Bool,
648            value: BinaryValue::Bool(true),
649        };
650
651        let bytes = entry.encode();
652        let (decoded, consumed) = BinaryEntry::decode(&bytes).unwrap();
653
654        assert_eq!(decoded, entry);
655        assert_eq!(consumed, bytes.len());
656    }
657
658    #[test]
659    fn test_decode_string() {
660        let entry = BinaryEntry {
661            fid: 1,
662            tag: TypeTag::String,
663            value: BinaryValue::String("hello".to_string()),
664        };
665
666        let bytes = entry.encode();
667        let (decoded, consumed) = BinaryEntry::decode(&bytes).unwrap();
668
669        assert_eq!(decoded, entry);
670        assert_eq!(consumed, bytes.len());
671    }
672
673    #[test]
674    fn test_decode_string_array() {
675        let entry = BinaryEntry {
676            fid: 23,
677            tag: TypeTag::StringArray,
678            value: BinaryValue::StringArray(vec!["admin".to_string(), "dev".to_string()]),
679        };
680
681        let bytes = entry.encode();
682        let (decoded, consumed) = BinaryEntry::decode(&bytes).unwrap();
683
684        assert_eq!(decoded, entry);
685        assert_eq!(consumed, bytes.len());
686    }
687
688    #[test]
689    fn test_decode_with_trailing_data() {
690        let entry = BinaryEntry {
691            fid: 1,
692            tag: TypeTag::Int,
693            value: BinaryValue::Int(42),
694        };
695
696        let mut bytes = entry.encode();
697        bytes.extend_from_slice(&[0xFF, 0xFF, 0xFF]); // Extra bytes
698
699        let (decoded, consumed) = BinaryEntry::decode(&bytes).unwrap();
700
701        assert_eq!(decoded, entry);
702        assert_eq!(consumed, bytes.len() - 3); // Should not consume trailing bytes
703    }
704
705    #[test]
706    fn test_decode_insufficient_data_fid() {
707        let bytes = vec![0x01]; // Only 1 byte, need 2 for FID
708        let result = BinaryEntry::decode(&bytes);
709        assert!(matches!(result, Err(BinaryError::UnexpectedEof { .. })));
710    }
711
712    #[test]
713    fn test_decode_insufficient_data_tag() {
714        let bytes = vec![0x01, 0x00]; // FID but no TAG
715        let result = BinaryEntry::decode(&bytes);
716        assert!(matches!(result, Err(BinaryError::UnexpectedEof { .. })));
717    }
718
719    #[test]
720    fn test_decode_invalid_type_tag() {
721        let bytes = vec![0x01, 0x00, 0xFF]; // Invalid TAG
722        let result = BinaryEntry::decode(&bytes);
723        assert!(matches!(result, Err(BinaryError::InvalidTypeTag { .. })));
724    }
725
726    #[test]
727    fn test_decode_invalid_bool_value() {
728        let bytes = vec![0x01, 0x00, 0x03, 0x02]; // Bool with value 0x02
729        let result = BinaryEntry::decode(&bytes);
730        assert!(matches!(result, Err(BinaryError::InvalidValue { .. })));
731    }
732
733    #[test]
734    fn test_decode_invalid_utf8() {
735        let mut bytes = vec![0x01, 0x00, 0x04]; // FID=1, TAG=String
736        bytes.extend_from_slice(&varint::encode(3)); // Length=3
737        bytes.extend_from_slice(&[0xFF, 0xFE, 0xFD]); // Invalid UTF-8
738
739        let result = BinaryEntry::decode(&bytes);
740        assert!(matches!(result, Err(BinaryError::InvalidUtf8 { .. })));
741    }
742
743    #[test]
744    fn test_roundtrip_all_types() {
745        let test_cases = vec![
746            BinaryEntry {
747                fid: 1,
748                tag: TypeTag::Int,
749                value: BinaryValue::Int(-42),
750            },
751            BinaryEntry {
752                fid: 2,
753                tag: TypeTag::Float,
754                value: BinaryValue::Float(3.14159),
755            },
756            BinaryEntry {
757                fid: 3,
758                tag: TypeTag::Bool,
759                value: BinaryValue::Bool(true),
760            },
761            BinaryEntry {
762                fid: 4,
763                tag: TypeTag::String,
764                value: BinaryValue::String("test".to_string()),
765            },
766            BinaryEntry {
767                fid: 5,
768                tag: TypeTag::StringArray,
769                value: BinaryValue::StringArray(vec!["a".to_string(), "b".to_string()]),
770            },
771        ];
772
773        for entry in test_cases {
774            let bytes = entry.encode();
775            let (decoded, _) = BinaryEntry::decode(&bytes).unwrap();
776            assert_eq!(decoded, entry);
777        }
778    }
779
780    #[test]
781    fn test_fid_boundary_values() {
782        // Test FID = 0
783        let entry0 = BinaryEntry {
784            fid: 0,
785            tag: TypeTag::Int,
786            value: BinaryValue::Int(1),
787        };
788        let bytes0 = entry0.encode();
789        let (decoded0, _) = BinaryEntry::decode(&bytes0).unwrap();
790        assert_eq!(decoded0.fid, 0);
791
792        // Test FID = 65535 (max u16)
793        let entry_max = BinaryEntry {
794            fid: 65535,
795            tag: TypeTag::Int,
796            value: BinaryValue::Int(1),
797        };
798        let bytes_max = entry_max.encode();
799        let (decoded_max, _) = BinaryEntry::decode(&bytes_max).unwrap();
800        assert_eq!(decoded_max.fid, 65535);
801    }
802
803    #[test]
804    fn test_empty_string() {
805        let entry = BinaryEntry {
806            fid: 1,
807            tag: TypeTag::String,
808            value: BinaryValue::String("".to_string()),
809        };
810
811        let bytes = entry.encode();
812        let (decoded, _) = BinaryEntry::decode(&bytes).unwrap();
813        assert_eq!(decoded, entry);
814    }
815
816    #[test]
817    fn test_empty_string_array() {
818        let entry = BinaryEntry {
819            fid: 1,
820            tag: TypeTag::StringArray,
821            value: BinaryValue::StringArray(vec![]),
822        };
823
824        let bytes = entry.encode();
825        let (decoded, _) = BinaryEntry::decode(&bytes).unwrap();
826        assert_eq!(decoded, entry);
827    }
828
829    #[test]
830    fn test_string_with_unicode() {
831        let entry = BinaryEntry {
832            fid: 1,
833            tag: TypeTag::String,
834            value: BinaryValue::String("Hello 🎯 World".to_string()),
835        };
836
837        let bytes = entry.encode();
838        let (decoded, _) = BinaryEntry::decode(&bytes).unwrap();
839        assert_eq!(decoded, entry);
840    }
841
842    #[test]
843    fn test_negative_int() {
844        let entry = BinaryEntry {
845            fid: 1,
846            tag: TypeTag::Int,
847            value: BinaryValue::Int(-9999),
848        };
849
850        let bytes = entry.encode();
851        let (decoded, _) = BinaryEntry::decode(&bytes).unwrap();
852        assert_eq!(decoded, entry);
853    }
854
855    #[test]
856    fn test_special_floats() {
857        // Test NaN
858        let entry_nan = BinaryEntry {
859            fid: 1,
860            tag: TypeTag::Float,
861            value: BinaryValue::Float(f64::NAN),
862        };
863        let bytes_nan = entry_nan.encode();
864        let (decoded_nan, _) = BinaryEntry::decode(&bytes_nan).unwrap();
865        match decoded_nan.value {
866            BinaryValue::Float(f) => assert!(f.is_nan()),
867            _ => panic!("Expected Float variant"),
868        }
869
870        // Test Infinity
871        let entry_inf = BinaryEntry {
872            fid: 2,
873            tag: TypeTag::Float,
874            value: BinaryValue::Float(f64::INFINITY),
875        };
876        let bytes_inf = entry_inf.encode();
877        let (decoded_inf, _) = BinaryEntry::decode(&bytes_inf).unwrap();
878        assert_eq!(decoded_inf, entry_inf);
879
880        // Test Negative Infinity
881        let entry_neg_inf = BinaryEntry {
882            fid: 3,
883            tag: TypeTag::Float,
884            value: BinaryValue::Float(f64::NEG_INFINITY),
885        };
886        let bytes_neg_inf = entry_neg_inf.encode();
887        let (decoded_neg_inf, _) = BinaryEntry::decode(&bytes_neg_inf).unwrap();
888        assert_eq!(decoded_neg_inf, entry_neg_inf);
889    }
890}