1use std::collections::HashMap;
4use std::fmt;
5use std::io;
6use indexmap::IndexMap;
7
8pub type ObjectMap<K, V> = IndexMap<K, V>;
10
11pub const MAGIC: [u8; 4] = *b"TLBX";
16pub const VERSION_MAJOR: u16 = 2;
18pub const VERSION_MINOR: u16 = 0;
20pub const VERSION: &str = "2.0.0-beta.8";
22pub const HEADER_SIZE: usize = 64;
23pub const MAX_STRING_LENGTH: usize = u32::MAX as usize;
25pub const MAX_OBJECT_FIELDS: usize = u16::MAX as usize;
27pub const MAX_ARRAY_LENGTH: usize = u32::MAX as usize;
29
30#[derive(Debug)]
35pub enum Error {
36 Io(io::Error),
37 InvalidMagic,
38 InvalidVersion { major: u16, minor: u16 },
39 InvalidType(u8),
40 InvalidUtf8,
41 UnexpectedToken { expected: String, got: String },
42 UnexpectedEof,
43 UnknownStruct(String),
44 MissingField(String),
45 ParseError(String),
46 ValueOutOfRange(String),
47}
48
49impl fmt::Display for Error {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self {
52 Error::Io(e) => write!(f, "IO error: {}", e),
53 Error::InvalidMagic => write!(f, "Invalid TeaLeaf magic bytes"),
54 Error::InvalidVersion { major, minor } => {
55 write!(f, "Unsupported version: {}.{}", major, minor)
56 }
57 Error::InvalidType(t) => write!(f, "Invalid type code: 0x{:02X}", t),
58 Error::InvalidUtf8 => write!(f, "Invalid UTF-8"),
59 Error::UnexpectedToken { expected, got } => {
60 write!(f, "Expected {}, got {}", expected, got)
61 }
62 Error::UnexpectedEof => write!(f, "Unexpected end of input"),
63 Error::UnknownStruct(s) => write!(f, "Unknown struct: {}", s),
64 Error::MissingField(s) => write!(f, "Missing field: {}", s),
65 Error::ParseError(s) => write!(f, "Parse error: {}", s),
66 Error::ValueOutOfRange(s) => write!(f, "Value out of range: {}", s),
67 }
68 }
69}
70
71impl std::error::Error for Error {}
72
73impl From<io::Error> for Error {
74 fn from(e: io::Error) -> Self {
75 Error::Io(e)
76 }
77}
78
79pub type Result<T> = std::result::Result<T, Error>;
80
81#[repr(u8)]
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
87pub enum TLType {
88 Null = 0x00,
89 Bool = 0x01,
90 Int8 = 0x02,
91 Int16 = 0x03,
92 Int32 = 0x04,
93 Int64 = 0x05,
94 UInt8 = 0x06,
95 UInt16 = 0x07,
96 UInt32 = 0x08,
97 UInt64 = 0x09,
98 Float32 = 0x0A,
99 Float64 = 0x0B,
100 String = 0x10,
101 Bytes = 0x11,
102 JsonNumber = 0x12,
103 Array = 0x20,
104 Object = 0x21,
105 Struct = 0x22,
106 Map = 0x23,
107 Tuple = 0x24,
108 Ref = 0x30,
109 Tagged = 0x31,
110 Timestamp = 0x32,
111}
112
113impl TryFrom<u8> for TLType {
114 type Error = Error;
115
116 fn try_from(v: u8) -> Result<Self> {
117 match v {
118 0x00 => Ok(Self::Null),
119 0x01 => Ok(Self::Bool),
120 0x02 => Ok(Self::Int8),
121 0x03 => Ok(Self::Int16),
122 0x04 => Ok(Self::Int32),
123 0x05 => Ok(Self::Int64),
124 0x06 => Ok(Self::UInt8),
125 0x07 => Ok(Self::UInt16),
126 0x08 => Ok(Self::UInt32),
127 0x09 => Ok(Self::UInt64),
128 0x0A => Ok(Self::Float32),
129 0x0B => Ok(Self::Float64),
130 0x10 => Ok(Self::String),
131 0x11 => Ok(Self::Bytes),
132 0x12 => Ok(Self::JsonNumber),
133 0x20 => Ok(Self::Array),
134 0x21 => Ok(Self::Object),
135 0x22 => Ok(Self::Struct),
136 0x23 => Ok(Self::Map),
137 0x24 => Ok(Self::Tuple),
138 0x30 => Ok(Self::Ref),
139 0x31 => Ok(Self::Tagged),
140 0x32 => Ok(Self::Timestamp),
141 _ => Err(Error::InvalidType(v)),
142 }
143 }
144}
145
146#[derive(Debug, Clone, PartialEq)]
151pub struct FieldType {
152 pub base: String,
153 pub nullable: bool,
154 pub is_array: bool,
155}
156
157impl FieldType {
158 pub fn new(base: impl Into<String>) -> Self {
159 Self {
160 base: base.into(),
161 nullable: false,
162 is_array: false,
163 }
164 }
165
166 pub fn nullable(mut self) -> Self {
167 self.nullable = true;
168 self
169 }
170
171 pub fn array(mut self) -> Self {
172 self.is_array = true;
173 self
174 }
175
176 pub fn parse(s: &str) -> Self {
177 let mut s = s.trim();
178 let mut nullable = false;
179 let mut is_array = false;
180
181 if s.ends_with('?') {
183 nullable = true;
184 s = &s[..s.len() - 1];
185 }
186
187 if s.starts_with("[]") {
189 is_array = true;
190 s = &s[2..];
191 }
192
193 Self {
194 base: s.to_string(),
195 nullable,
196 is_array,
197 }
198 }
199
200 pub fn to_tl_type(&self) -> TLType {
201 if self.is_array {
202 return TLType::Array;
203 }
204 match self.base.as_str() {
205 "bool" => TLType::Bool,
206 "int8" => TLType::Int8,
207 "int16" => TLType::Int16,
208 "int" | "int32" => TLType::Int32,
209 "int64" => TLType::Int64,
210 "uint8" => TLType::UInt8,
211 "uint16" => TLType::UInt16,
212 "uint" | "uint32" => TLType::UInt32,
213 "uint64" => TLType::UInt64,
214 "float32" => TLType::Float32,
215 "float" | "float64" => TLType::Float64,
216 "string" => TLType::String,
217 "bytes" => TLType::Bytes,
218 "timestamp" => TLType::Timestamp,
219 "object" => TLType::Object,
220 "tuple" => TLType::Tuple,
221 "map" => TLType::Map,
222 _ => TLType::Struct, }
224 }
225
226 pub fn is_struct(&self) -> bool {
227 !self.is_array && self.to_tl_type() == TLType::Struct
228 }
229}
230
231impl fmt::Display for FieldType {
232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233 if self.is_array {
234 write!(f, "[]")?;
235 }
236 write!(f, "{}", self.base)?;
237 if self.nullable {
238 write!(f, "?")?;
239 }
240 Ok(())
241 }
242}
243
244#[derive(Debug, Clone)]
249pub struct Field {
250 pub name: String,
251 pub field_type: FieldType,
252}
253
254impl Field {
255 pub fn new(name: impl Into<String>, field_type: FieldType) -> Self {
256 Self {
257 name: name.into(),
258 field_type,
259 }
260 }
261}
262
263#[derive(Debug, Clone)]
264pub struct Schema {
265 pub name: String,
266 pub fields: Vec<Field>,
267}
268
269impl Schema {
270 pub fn new(name: impl Into<String>) -> Self {
271 Self {
272 name: name.into(),
273 fields: Vec::new(),
274 }
275 }
276
277 pub fn field(mut self, name: impl Into<String>, field_type: FieldType) -> Self {
278 self.fields.push(Field::new(name, field_type));
279 self
280 }
281
282 pub fn add_field(&mut self, name: impl Into<String>, field_type: FieldType) {
283 self.fields.push(Field::new(name, field_type));
284 }
285
286 pub fn get_field(&self, name: &str) -> Option<&Field> {
287 self.fields.iter().find(|f| f.name == name)
288 }
289
290 pub fn field_index(&self, name: &str) -> Option<usize> {
291 self.fields.iter().position(|f| f.name == name)
292 }
293}
294
295#[derive(Debug, Clone)]
301pub struct Variant {
302 pub name: String,
303 pub fields: Vec<Field>,
304}
305
306impl Variant {
307 pub fn new(name: impl Into<String>) -> Self {
308 Self {
309 name: name.into(),
310 fields: Vec::new(),
311 }
312 }
313
314 pub fn field(mut self, name: impl Into<String>, field_type: FieldType) -> Self {
315 self.fields.push(Field::new(name, field_type));
316 self
317 }
318}
319
320#[derive(Debug, Clone)]
322pub struct Union {
323 pub name: String,
324 pub variants: Vec<Variant>,
325}
326
327impl Union {
328 pub fn new(name: impl Into<String>) -> Self {
329 Self {
330 name: name.into(),
331 variants: Vec::new(),
332 }
333 }
334
335 pub fn variant(mut self, variant: Variant) -> Self {
336 self.variants.push(variant);
337 self
338 }
339
340 pub fn add_variant(&mut self, variant: Variant) {
341 self.variants.push(variant);
342 }
343
344 pub fn get_variant(&self, name: &str) -> Option<&Variant> {
345 self.variants.iter().find(|v| v.name == name)
346 }
347}
348
349#[derive(Debug, Clone, PartialEq)]
354pub enum Value {
355 Null,
356 Bool(bool),
357 Int(i64),
358 UInt(u64),
359 Float(f64),
360 String(String),
361 Bytes(Vec<u8>),
362 Array(Vec<Value>),
363 Object(ObjectMap<String, Value>),
364 Map(Vec<(Value, Value)>), Ref(String),
366 Tagged(String, Box<Value>),
367 Timestamp(i64, i16), JsonNumber(String), }
370
371impl Value {
372 pub fn is_null(&self) -> bool {
373 matches!(self, Value::Null)
374 }
375
376 pub fn as_bool(&self) -> Option<bool> {
377 match self {
378 Value::Bool(b) => Some(*b),
379 _ => None,
380 }
381 }
382
383 pub fn as_int(&self) -> Option<i64> {
384 match self {
385 Value::Int(i) => Some(*i),
386 Value::UInt(u) if *u <= i64::MAX as u64 => Some(*u as i64),
387 Value::JsonNumber(s) => s.parse::<i64>().ok(),
388 _ => None,
389 }
390 }
391
392 pub fn as_int_checked(&self) -> Result<i64> {
393 match self {
394 Value::Int(i) => Ok(*i),
395 Value::UInt(u) if *u <= i64::MAX as u64 => Ok(*u as i64),
396 Value::UInt(u) => Err(Error::ValueOutOfRange(
397 format!("uint {} exceeds i64::MAX", u),
398 )),
399 Value::JsonNumber(s) => s.parse::<i64>().map_err(|_| Error::ValueOutOfRange(
400 format!("json number '{}' does not fit in i64", s),
401 )),
402 _ => Err(Error::ValueOutOfRange(
403 format!("cannot convert {:?} to i64", self),
404 )),
405 }
406 }
407
408 pub fn as_uint(&self) -> Option<u64> {
409 match self {
410 Value::UInt(u) => Some(*u),
411 Value::Int(i) if *i >= 0 => Some(*i as u64),
412 Value::JsonNumber(s) => s.parse::<u64>().ok(),
413 _ => None,
414 }
415 }
416
417 pub fn as_float(&self) -> Option<f64> {
418 match self {
419 Value::Float(f) => Some(*f),
420 Value::Int(i) => Some(*i as f64),
421 Value::UInt(u) => Some(*u as f64),
422 Value::JsonNumber(s) => s.parse::<f64>().ok(),
423 _ => None,
424 }
425 }
426
427 pub fn as_str(&self) -> Option<&str> {
428 match self {
429 Value::String(s) => Some(s),
430 Value::JsonNumber(s) => Some(s),
431 _ => None,
432 }
433 }
434
435 pub fn as_bytes(&self) -> Option<&[u8]> {
436 match self {
437 Value::Bytes(b) => Some(b),
438 _ => None,
439 }
440 }
441
442 pub fn as_array(&self) -> Option<&[Value]> {
443 match self {
444 Value::Array(arr) => Some(arr),
445 _ => None,
446 }
447 }
448
449 pub fn as_object(&self) -> Option<&ObjectMap<String, Value>> {
450 match self {
451 Value::Object(obj) => Some(obj),
452 _ => None,
453 }
454 }
455
456 pub fn get(&self, key: &str) -> Option<&Value> {
457 self.as_object()?.get(key)
458 }
459
460 pub fn index(&self, idx: usize) -> Option<&Value> {
461 self.as_array()?.get(idx)
462 }
463
464 pub fn tl_type(&self) -> TLType {
465 match self {
466 Value::Null => TLType::Null,
467 Value::Bool(_) => TLType::Bool,
468 Value::Int(i) => {
469 if *i >= i8::MIN as i64 && *i <= i8::MAX as i64 {
470 TLType::Int8
471 } else if *i >= i16::MIN as i64 && *i <= i16::MAX as i64 {
472 TLType::Int16
473 } else if *i >= i32::MIN as i64 && *i <= i32::MAX as i64 {
474 TLType::Int32
475 } else {
476 TLType::Int64
477 }
478 }
479 Value::UInt(u) => {
480 if *u <= u8::MAX as u64 {
481 TLType::UInt8
482 } else if *u <= u16::MAX as u64 {
483 TLType::UInt16
484 } else if *u <= u32::MAX as u64 {
485 TLType::UInt32
486 } else {
487 TLType::UInt64
488 }
489 }
490 Value::Float(_) => TLType::Float64,
491 Value::String(_) => TLType::String,
492 Value::Bytes(_) => TLType::Bytes,
493 Value::Array(_) => TLType::Array,
494 Value::Object(_) => TLType::Object,
495 Value::Map(_) => TLType::Map,
496 Value::Ref(_) => TLType::Ref,
497 Value::Tagged(_, _) => TLType::Tagged,
498 Value::Timestamp(_, _) => TLType::Timestamp,
499 Value::JsonNumber(_) => TLType::JsonNumber,
500 }
501 }
502
503 pub fn as_timestamp(&self) -> Option<(i64, i16)> {
504 match self {
505 Value::Timestamp(ts, tz) => Some((*ts, *tz)),
506 _ => None,
507 }
508 }
509
510 pub fn as_timestamp_millis(&self) -> Option<i64> {
511 match self {
512 Value::Timestamp(ts, _) => Some(*ts),
513 _ => None,
514 }
515 }
516
517 pub fn as_map(&self) -> Option<&[(Value, Value)]> {
518 match self {
519 Value::Map(m) => Some(m),
520 _ => None,
521 }
522 }
523
524 pub fn as_ref_name(&self) -> Option<&str> {
525 match self {
526 Value::Ref(name) => Some(name),
527 _ => None,
528 }
529 }
530
531 pub fn as_tagged(&self) -> Option<(&str, &Value)> {
532 match self {
533 Value::Tagged(tag, value) => Some((tag, value)),
534 _ => None,
535 }
536 }
537
538 pub fn as_json_number(&self) -> Option<&str> {
539 match self {
540 Value::JsonNumber(s) => Some(s),
541 _ => None,
542 }
543 }
544}
545
546impl Default for Value {
547 fn default() -> Self {
548 Value::Null
549 }
550}
551
552impl From<bool> for Value {
554 fn from(b: bool) -> Self { Value::Bool(b) }
555}
556
557impl From<i32> for Value {
558 fn from(i: i32) -> Self { Value::Int(i as i64) }
559}
560
561impl From<i64> for Value {
562 fn from(i: i64) -> Self { Value::Int(i) }
563}
564
565impl From<u32> for Value {
566 fn from(u: u32) -> Self { Value::UInt(u as u64) }
567}
568
569impl From<u64> for Value {
570 fn from(u: u64) -> Self { Value::UInt(u) }
571}
572
573impl From<f64> for Value {
574 fn from(f: f64) -> Self { Value::Float(f) }
575}
576
577impl From<String> for Value {
578 fn from(s: String) -> Self { Value::String(s) }
579}
580
581impl From<&str> for Value {
582 fn from(s: &str) -> Self { Value::String(s.to_string()) }
583}
584
585impl<T: Into<Value>> From<Vec<T>> for Value {
586 fn from(v: Vec<T>) -> Self {
587 Value::Array(v.into_iter().map(Into::into).collect())
588 }
589}
590
591impl From<ObjectMap<String, Value>> for Value {
592 fn from(m: ObjectMap<String, Value>) -> Self {
593 Value::Object(m)
594 }
595}
596
597impl From<HashMap<String, Value>> for Value {
598 fn from(m: HashMap<String, Value>) -> Self {
599 Value::Object(m.into_iter().collect())
600 }
601}
602
603#[cfg(test)]
608mod tests {
609 use super::*;
610
611 #[test]
616 fn test_tltype_try_from_all_valid() {
617 let cases: Vec<(u8, TLType)> = vec![
618 (0x00, TLType::Null),
619 (0x01, TLType::Bool),
620 (0x02, TLType::Int8),
621 (0x03, TLType::Int16),
622 (0x04, TLType::Int32),
623 (0x05, TLType::Int64),
624 (0x06, TLType::UInt8),
625 (0x07, TLType::UInt16),
626 (0x08, TLType::UInt32),
627 (0x09, TLType::UInt64),
628 (0x0A, TLType::Float32),
629 (0x0B, TLType::Float64),
630 (0x10, TLType::String),
631 (0x11, TLType::Bytes),
632 (0x20, TLType::Array),
633 (0x21, TLType::Object),
634 (0x22, TLType::Struct),
635 (0x23, TLType::Map),
636 (0x24, TLType::Tuple),
637 (0x30, TLType::Ref),
638 (0x31, TLType::Tagged),
639 (0x32, TLType::Timestamp),
640 (0x12, TLType::JsonNumber),
641 ];
642 for (byte, expected) in cases {
643 assert_eq!(TLType::try_from(byte).unwrap(), expected, "byte=0x{:02X}", byte);
644 }
645 }
646
647 #[test]
648 fn test_tltype_try_from_invalid() {
649 let err = TLType::try_from(0xFF).unwrap_err();
650 assert!(matches!(err, Error::InvalidType(0xFF)));
651
652 let err2 = TLType::try_from(0x0C).unwrap_err();
653 assert!(matches!(err2, Error::InvalidType(0x0C)));
654 }
655
656 #[test]
661 fn test_fieldtype_new() {
662 let ft = FieldType::new("int");
663 assert_eq!(ft.base, "int");
664 assert!(!ft.nullable);
665 assert!(!ft.is_array);
666 }
667
668 #[test]
669 fn test_fieldtype_nullable() {
670 let ft = FieldType::new("string").nullable();
671 assert!(ft.nullable);
672 assert!(!ft.is_array);
673 }
674
675 #[test]
676 fn test_fieldtype_array() {
677 let ft = FieldType::new("int").array();
678 assert!(ft.is_array);
679 assert!(!ft.nullable);
680 }
681
682 #[test]
683 fn test_fieldtype_parse_simple() {
684 let ft = FieldType::parse("int");
685 assert_eq!(ft.base, "int");
686 assert!(!ft.nullable);
687 assert!(!ft.is_array);
688 }
689
690 #[test]
691 fn test_fieldtype_parse_nullable() {
692 let ft = FieldType::parse("string?");
693 assert_eq!(ft.base, "string");
694 assert!(ft.nullable);
695 assert!(!ft.is_array);
696 }
697
698 #[test]
699 fn test_fieldtype_parse_array() {
700 let ft = FieldType::parse("[]int");
701 assert_eq!(ft.base, "int");
702 assert!(!ft.nullable);
703 assert!(ft.is_array);
704 }
705
706 #[test]
707 fn test_fieldtype_parse_array_nullable() {
708 let ft = FieldType::parse("[]string?");
709 assert_eq!(ft.base, "string");
710 assert!(ft.nullable);
711 assert!(ft.is_array);
712 }
713
714 #[test]
715 fn test_fieldtype_parse_with_whitespace() {
716 let ft = FieldType::parse(" int64 ");
717 assert_eq!(ft.base, "int64");
718 }
719
720 #[test]
721 fn test_fieldtype_display() {
722 assert_eq!(FieldType::new("int").to_string(), "int");
723 assert_eq!(FieldType::new("string").nullable().to_string(), "string?");
724 assert_eq!(FieldType::new("int").array().to_string(), "[]int");
725 assert_eq!(
726 FieldType::new("string").array().nullable().to_string(),
727 "[]string?"
728 );
729 }
730
731 #[test]
732 fn test_fieldtype_to_tl_type_all_bases() {
733 let cases = vec![
734 ("bool", TLType::Bool),
735 ("int8", TLType::Int8),
736 ("int16", TLType::Int16),
737 ("int", TLType::Int32),
738 ("int32", TLType::Int32),
739 ("int64", TLType::Int64),
740 ("uint8", TLType::UInt8),
741 ("uint16", TLType::UInt16),
742 ("uint", TLType::UInt32),
743 ("uint32", TLType::UInt32),
744 ("uint64", TLType::UInt64),
745 ("float32", TLType::Float32),
746 ("float", TLType::Float64),
747 ("float64", TLType::Float64),
748 ("string", TLType::String),
749 ("bytes", TLType::Bytes),
750 ("timestamp", TLType::Timestamp),
751 ("object", TLType::Object),
752 ("tuple", TLType::Tuple),
753 ("map", TLType::Map),
754 ("MyStruct", TLType::Struct),
755 ("SomeUnknown", TLType::Struct),
756 ];
757 for (base, expected) in cases {
758 let ft = FieldType::new(base);
759 assert_eq!(ft.to_tl_type(), expected, "base={}", base);
760 }
761 }
762
763 #[test]
764 fn test_fieldtype_array_overrides_base() {
765 let ft = FieldType::new("int").array();
766 assert_eq!(ft.to_tl_type(), TLType::Array);
767 }
768
769 #[test]
770 fn test_fieldtype_is_struct() {
771 assert!(FieldType::new("MyStruct").is_struct());
772 assert!(!FieldType::new("int").is_struct());
773 assert!(!FieldType::new("string").is_struct());
774 assert!(!FieldType::new("int").array().is_struct()); }
776
777 #[test]
782 fn test_schema_builder() {
783 let schema = Schema::new("User")
784 .field("id", FieldType::new("int64"))
785 .field("name", FieldType::new("string"));
786 assert_eq!(schema.name, "User");
787 assert_eq!(schema.fields.len(), 2);
788 assert_eq!(schema.fields[0].name, "id");
789 assert_eq!(schema.fields[1].name, "name");
790 }
791
792 #[test]
793 fn test_schema_add_field() {
794 let mut schema = Schema::new("Event");
795 schema.add_field("ts", FieldType::new("timestamp"));
796 assert_eq!(schema.fields.len(), 1);
797 assert_eq!(schema.fields[0].name, "ts");
798 }
799
800 #[test]
801 fn test_schema_get_field_found() {
802 let schema = Schema::new("User")
803 .field("id", FieldType::new("int64"))
804 .field("name", FieldType::new("string"));
805 let f = schema.get_field("name").unwrap();
806 assert_eq!(f.name, "name");
807 assert_eq!(f.field_type.base, "string");
808 }
809
810 #[test]
811 fn test_schema_get_field_missing() {
812 let schema = Schema::new("User")
813 .field("id", FieldType::new("int64"));
814 assert!(schema.get_field("nonexistent").is_none());
815 }
816
817 #[test]
818 fn test_schema_field_index_found() {
819 let schema = Schema::new("User")
820 .field("id", FieldType::new("int64"))
821 .field("name", FieldType::new("string"));
822 assert_eq!(schema.field_index("id"), Some(0));
823 assert_eq!(schema.field_index("name"), Some(1));
824 }
825
826 #[test]
827 fn test_schema_field_index_missing() {
828 let schema = Schema::new("User")
829 .field("id", FieldType::new("int64"));
830 assert_eq!(schema.field_index("missing"), None);
831 }
832
833 #[test]
838 fn test_variant_builder() {
839 let v = Variant::new("Circle")
840 .field("radius", FieldType::new("float"));
841 assert_eq!(v.name, "Circle");
842 assert_eq!(v.fields.len(), 1);
843 assert_eq!(v.fields[0].name, "radius");
844 }
845
846 #[test]
847 fn test_union_builder() {
848 let u = Union::new("Shape")
849 .variant(Variant::new("Circle").field("radius", FieldType::new("float")))
850 .variant(Variant::new("Point"));
851 assert_eq!(u.name, "Shape");
852 assert_eq!(u.variants.len(), 2);
853 }
854
855 #[test]
856 fn test_union_add_variant() {
857 let mut u = Union::new("Shape");
858 u.add_variant(Variant::new("Circle"));
859 assert_eq!(u.variants.len(), 1);
860 }
861
862 #[test]
863 fn test_union_get_variant() {
864 let u = Union::new("Shape")
865 .variant(Variant::new("Circle").field("radius", FieldType::new("float")))
866 .variant(Variant::new("Point"));
867 assert!(u.get_variant("Circle").is_some());
868 assert!(u.get_variant("Point").is_some());
869 assert!(u.get_variant("Unknown").is_none());
870 }
871
872 #[test]
877 fn test_value_is_null() {
878 assert!(Value::Null.is_null());
879 assert!(!Value::Bool(false).is_null());
880 }
881
882 #[test]
883 fn test_value_as_bool() {
884 assert_eq!(Value::Bool(true).as_bool(), Some(true));
885 assert_eq!(Value::Int(1).as_bool(), None);
886 }
887
888 #[test]
889 fn test_value_as_int_from_uint() {
890 assert_eq!(Value::UInt(42).as_int(), Some(42));
892 }
893
894 #[test]
895 fn test_value_as_int_overflow_returns_none() {
896 assert_eq!(Value::UInt(u64::MAX).as_int(), None);
898 assert_eq!(Value::UInt(i64::MAX as u64 + 1).as_int(), None);
899 assert_eq!(Value::UInt(i64::MAX as u64).as_int(), Some(i64::MAX));
901 }
902
903 #[test]
904 fn test_value_as_int_checked_success() {
905 assert_eq!(Value::Int(42).as_int_checked().unwrap(), 42);
906 assert_eq!(Value::UInt(42).as_int_checked().unwrap(), 42);
907 assert_eq!(Value::UInt(i64::MAX as u64).as_int_checked().unwrap(), i64::MAX);
908 }
909
910 #[test]
911 fn test_value_as_int_checked_overflow_error() {
912 let result = Value::UInt(u64::MAX).as_int_checked();
913 assert!(result.is_err());
914 assert!(matches!(result.unwrap_err(), Error::ValueOutOfRange(_)));
915 }
916
917 #[test]
918 fn test_value_as_int_checked_wrong_type_error() {
919 let result = Value::String("nope".into()).as_int_checked();
920 assert!(result.is_err());
921 assert!(matches!(result.unwrap_err(), Error::ValueOutOfRange(_)));
922 }
923
924 #[test]
925 fn test_value_as_int_from_non_numeric() {
926 assert_eq!(Value::String("nope".into()).as_int(), None);
927 }
928
929 #[test]
930 fn test_value_as_uint_from_positive_int() {
931 assert_eq!(Value::Int(42).as_uint(), Some(42));
933 }
934
935 #[test]
936 fn test_value_as_uint_from_negative_int() {
937 assert_eq!(Value::Int(-1).as_uint(), None);
939 }
940
941 #[test]
942 fn test_value_as_uint_from_non_numeric() {
943 assert_eq!(Value::Bool(true).as_uint(), None);
944 }
945
946 #[test]
947 fn test_value_as_float_from_int() {
948 assert_eq!(Value::Int(42).as_float(), Some(42.0));
949 }
950
951 #[test]
952 fn test_value_as_float_from_uint() {
953 assert_eq!(Value::UInt(100).as_float(), Some(100.0));
954 }
955
956 #[test]
957 fn test_value_as_float_from_non_numeric() {
958 assert_eq!(Value::String("no".into()).as_float(), None);
959 }
960
961 #[test]
962 fn test_value_as_str() {
963 assert_eq!(Value::String("hello".into()).as_str(), Some("hello"));
964 assert_eq!(Value::Int(1).as_str(), None);
965 }
966
967 #[test]
968 fn test_value_as_bytes() {
969 let data = vec![1u8, 2, 3];
970 assert_eq!(Value::Bytes(data.clone()).as_bytes(), Some(data.as_slice()));
971 assert_eq!(Value::Null.as_bytes(), None);
972 }
973
974 #[test]
975 fn test_value_as_array() {
976 let arr = vec![Value::Int(1), Value::Int(2)];
977 assert_eq!(
978 Value::Array(arr.clone()).as_array(),
979 Some(arr.as_slice())
980 );
981 assert_eq!(Value::Null.as_array(), None);
982 }
983
984 #[test]
985 fn test_value_as_object() {
986 let mut obj = ObjectMap::new();
987 obj.insert("k".to_string(), Value::Int(1));
988 assert!(Value::Object(obj).as_object().is_some());
989 assert!(Value::Null.as_object().is_none());
990 }
991
992 #[test]
993 fn test_value_get() {
994 let mut obj = ObjectMap::new();
995 obj.insert("key".to_string(), Value::Int(42));
996 let val = Value::Object(obj);
997 assert_eq!(val.get("key"), Some(&Value::Int(42)));
998 assert_eq!(val.get("missing"), None);
999 assert_eq!(Value::Int(1).get("x"), None);
1000 }
1001
1002 #[test]
1003 fn test_value_index() {
1004 let val = Value::Array(vec![Value::Int(10), Value::Int(20)]);
1005 assert_eq!(val.index(0), Some(&Value::Int(10)));
1006 assert_eq!(val.index(1), Some(&Value::Int(20)));
1007 assert_eq!(val.index(5), None);
1008 assert_eq!(Value::Int(1).index(0), None);
1009 }
1010
1011 #[test]
1012 fn test_value_as_timestamp() {
1013 assert_eq!(Value::Timestamp(1700000000, 0).as_timestamp(), Some((1700000000, 0)));
1014 assert_eq!(Value::Int(42).as_timestamp(), None);
1015 }
1016
1017 #[test]
1018 fn test_value_as_map() {
1019 let pairs = vec![(Value::String("k".into()), Value::Int(1))];
1020 assert_eq!(
1021 Value::Map(pairs.clone()).as_map(),
1022 Some(pairs.as_slice())
1023 );
1024 assert_eq!(Value::Null.as_map(), None);
1025 }
1026
1027 #[test]
1028 fn test_value_as_ref_name() {
1029 assert_eq!(Value::Ref("MyRef".into()).as_ref_name(), Some("MyRef"));
1030 assert_eq!(Value::Null.as_ref_name(), None);
1031 }
1032
1033 #[test]
1034 fn test_value_as_tagged() {
1035 let val = Value::Tagged("tag".into(), Box::new(Value::Int(1)));
1036 let (tag, inner) = val.as_tagged().unwrap();
1037 assert_eq!(tag, "tag");
1038 assert_eq!(inner, &Value::Int(1));
1039 assert_eq!(Value::Null.as_tagged(), None);
1040 }
1041
1042 #[test]
1043 fn test_value_default() {
1044 assert_eq!(Value::default(), Value::Null);
1045 }
1046
1047 #[test]
1052 fn test_value_tl_type_int_boundaries() {
1053 assert_eq!(Value::Int(0).tl_type(), TLType::Int8);
1055 assert_eq!(Value::Int(127).tl_type(), TLType::Int8);
1056 assert_eq!(Value::Int(-128).tl_type(), TLType::Int8);
1057
1058 assert_eq!(Value::Int(128).tl_type(), TLType::Int16);
1060 assert_eq!(Value::Int(-129).tl_type(), TLType::Int16);
1061 assert_eq!(Value::Int(32767).tl_type(), TLType::Int16);
1062 assert_eq!(Value::Int(-32768).tl_type(), TLType::Int16);
1063
1064 assert_eq!(Value::Int(32768).tl_type(), TLType::Int32);
1066 assert_eq!(Value::Int(-32769).tl_type(), TLType::Int32);
1067 assert_eq!(Value::Int(i32::MAX as i64).tl_type(), TLType::Int32);
1068 assert_eq!(Value::Int(i32::MIN as i64).tl_type(), TLType::Int32);
1069
1070 assert_eq!(Value::Int(i32::MAX as i64 + 1).tl_type(), TLType::Int64);
1072 assert_eq!(Value::Int(i32::MIN as i64 - 1).tl_type(), TLType::Int64);
1073 assert_eq!(Value::Int(i64::MAX).tl_type(), TLType::Int64);
1074 assert_eq!(Value::Int(i64::MIN).tl_type(), TLType::Int64);
1075 }
1076
1077 #[test]
1078 fn test_value_tl_type_uint_boundaries() {
1079 assert_eq!(Value::UInt(0).tl_type(), TLType::UInt8);
1081 assert_eq!(Value::UInt(255).tl_type(), TLType::UInt8);
1082
1083 assert_eq!(Value::UInt(256).tl_type(), TLType::UInt16);
1085 assert_eq!(Value::UInt(65535).tl_type(), TLType::UInt16);
1086
1087 assert_eq!(Value::UInt(65536).tl_type(), TLType::UInt32);
1089 assert_eq!(Value::UInt(u32::MAX as u64).tl_type(), TLType::UInt32);
1090
1091 assert_eq!(Value::UInt(u32::MAX as u64 + 1).tl_type(), TLType::UInt64);
1093 assert_eq!(Value::UInt(u64::MAX).tl_type(), TLType::UInt64);
1094 }
1095
1096 #[test]
1097 fn test_value_tl_type_other_variants() {
1098 assert_eq!(Value::Null.tl_type(), TLType::Null);
1099 assert_eq!(Value::Bool(true).tl_type(), TLType::Bool);
1100 assert_eq!(Value::Float(1.0).tl_type(), TLType::Float64);
1101 assert_eq!(Value::String("s".into()).tl_type(), TLType::String);
1102 assert_eq!(Value::Bytes(vec![]).tl_type(), TLType::Bytes);
1103 assert_eq!(Value::Array(vec![]).tl_type(), TLType::Array);
1104 assert_eq!(Value::Object(ObjectMap::new()).tl_type(), TLType::Object);
1105 assert_eq!(Value::Map(vec![]).tl_type(), TLType::Map);
1106 assert_eq!(Value::Ref("r".into()).tl_type(), TLType::Ref);
1107 assert_eq!(
1108 Value::Tagged("t".into(), Box::new(Value::Null)).tl_type(),
1109 TLType::Tagged
1110 );
1111 assert_eq!(Value::Timestamp(0, 0).tl_type(), TLType::Timestamp);
1112 assert_eq!(
1113 Value::JsonNumber("123.456".into()).tl_type(),
1114 TLType::JsonNumber
1115 );
1116 }
1117
1118 #[test]
1123 fn test_value_from_bool() {
1124 assert_eq!(Value::from(true), Value::Bool(true));
1125 assert_eq!(Value::from(false), Value::Bool(false));
1126 }
1127
1128 #[test]
1129 fn test_value_from_i32() {
1130 assert_eq!(Value::from(42i32), Value::Int(42));
1131 assert_eq!(Value::from(-1i32), Value::Int(-1));
1132 }
1133
1134 #[test]
1135 fn test_value_from_i64() {
1136 assert_eq!(Value::from(42i64), Value::Int(42));
1137 }
1138
1139 #[test]
1140 fn test_value_from_u32() {
1141 assert_eq!(Value::from(42u32), Value::UInt(42));
1142 }
1143
1144 #[test]
1145 fn test_value_from_u64() {
1146 assert_eq!(Value::from(42u64), Value::UInt(42));
1147 }
1148
1149 #[test]
1150 fn test_value_from_f64() {
1151 assert_eq!(Value::from(3.14f64), Value::Float(3.14));
1152 }
1153
1154 #[test]
1155 fn test_value_from_string() {
1156 assert_eq!(Value::from("hello".to_string()), Value::String("hello".into()));
1157 }
1158
1159 #[test]
1160 fn test_value_from_str() {
1161 assert_eq!(Value::from("hello"), Value::String("hello".into()));
1162 }
1163
1164 #[test]
1165 fn test_value_from_vec() {
1166 let v: Vec<i32> = vec![1, 2, 3];
1167 assert_eq!(
1168 Value::from(v),
1169 Value::Array(vec![Value::Int(1), Value::Int(2), Value::Int(3)])
1170 );
1171 }
1172
1173 #[test]
1174 fn test_value_from_objectmap() {
1175 let mut m = ObjectMap::new();
1176 m.insert("key".to_string(), Value::Int(42));
1177 let val = Value::from(m.clone());
1178 assert_eq!(val, Value::Object(m));
1179 }
1180
1181 #[test]
1182 fn test_value_from_hashmap() {
1183 let mut m = HashMap::new();
1184 m.insert("key".to_string(), Value::Int(42));
1185 let val = Value::from(m);
1186 assert!(matches!(val, Value::Object(_)));
1187 assert_eq!(val.as_object().unwrap().get("key").unwrap().as_int(), Some(42));
1188 }
1189
1190 #[test]
1195 fn test_error_display_all_variants() {
1196 assert_eq!(
1197 Error::Io(io::Error::new(io::ErrorKind::NotFound, "gone")).to_string(),
1198 "IO error: gone"
1199 );
1200 assert_eq!(Error::InvalidMagic.to_string(), "Invalid TeaLeaf magic bytes");
1201 assert_eq!(
1202 Error::InvalidVersion { major: 99, minor: 1 }.to_string(),
1203 "Unsupported version: 99.1"
1204 );
1205 assert_eq!(
1206 Error::InvalidType(0xFF).to_string(),
1207 "Invalid type code: 0xFF"
1208 );
1209 assert_eq!(Error::InvalidUtf8.to_string(), "Invalid UTF-8");
1210 assert_eq!(
1211 Error::UnexpectedToken {
1212 expected: "number".into(),
1213 got: "string".into()
1214 }
1215 .to_string(),
1216 "Expected number, got string"
1217 );
1218 assert_eq!(Error::UnexpectedEof.to_string(), "Unexpected end of input");
1219 assert_eq!(
1220 Error::UnknownStruct("Foo".into()).to_string(),
1221 "Unknown struct: Foo"
1222 );
1223 assert_eq!(
1224 Error::MissingField("bar".into()).to_string(),
1225 "Missing field: bar"
1226 );
1227 assert_eq!(
1228 Error::ParseError("bad input".into()).to_string(),
1229 "Parse error: bad input"
1230 );
1231 assert_eq!(
1232 Error::ValueOutOfRange("too big".into()).to_string(),
1233 "Value out of range: too big"
1234 );
1235 }
1236
1237 #[test]
1238 fn test_error_from_io() {
1239 let io_err = io::Error::new(io::ErrorKind::PermissionDenied, "denied");
1240 let tl_err = Error::from(io_err);
1241 assert!(matches!(tl_err, Error::Io(_)));
1242 assert!(tl_err.to_string().contains("denied"));
1243 }
1244
1245 #[test]
1250 fn test_field_new() {
1251 let f = Field::new("age", FieldType::new("int"));
1252 assert_eq!(f.name, "age");
1253 assert_eq!(f.field_type.base, "int");
1254 }
1255}