Skip to main content

ironsbe_schema/
types.rs

1//! Schema type definitions.
2//!
3//! This module contains the data structures representing SBE schema elements
4//! including primitives, composites, enums, and sets.
5
6use std::collections::HashMap;
7
8/// Complete SBE schema definition.
9#[derive(Debug, Clone)]
10pub struct Schema {
11    /// Package name (namespace).
12    pub package: String,
13    /// Schema identifier.
14    pub id: u16,
15    /// Schema version.
16    pub version: u16,
17    /// Semantic version string.
18    pub semantic_version: String,
19    /// Schema description.
20    pub description: Option<String>,
21    /// Byte order for encoding.
22    pub byte_order: ByteOrder,
23    /// Header type name.
24    pub header_type: String,
25    /// Type definitions.
26    pub types: Vec<TypeDef>,
27    /// Message definitions.
28    pub messages: Vec<super::messages::MessageDef>,
29    /// Type lookup map (built during parsing).
30    type_map: HashMap<String, usize>,
31}
32
33impl Schema {
34    /// Creates a new empty schema.
35    #[must_use]
36    pub fn new(package: String, id: u16, version: u16) -> Self {
37        Self {
38            package,
39            id,
40            version,
41            semantic_version: String::new(),
42            description: None,
43            byte_order: ByteOrder::LittleEndian,
44            header_type: "messageHeader".to_string(),
45            types: Vec::new(),
46            messages: Vec::new(),
47            type_map: HashMap::new(),
48        }
49    }
50
51    /// Adds a type definition to the schema.
52    pub fn add_type(&mut self, type_def: TypeDef) {
53        let name = type_def.name().to_string();
54        let index = self.types.len();
55        self.types.push(type_def);
56        self.type_map.insert(name, index);
57    }
58
59    /// Looks up a type by name.
60    #[must_use]
61    pub fn get_type(&self, name: &str) -> Option<&TypeDef> {
62        self.type_map.get(name).map(|&idx| &self.types[idx])
63    }
64
65    /// Returns true if a type with the given name exists.
66    #[must_use]
67    pub fn has_type(&self, name: &str) -> bool {
68        self.type_map.contains_key(name)
69    }
70
71    /// Builds the type lookup map from the types vector.
72    pub fn build_type_map(&mut self) {
73        self.type_map.clear();
74        for (idx, type_def) in self.types.iter().enumerate() {
75            self.type_map.insert(type_def.name().to_string(), idx);
76        }
77    }
78}
79
80/// Byte order for SBE encoding.
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
82pub enum ByteOrder {
83    /// Little-endian byte order (default for SBE).
84    #[default]
85    LittleEndian,
86    /// Big-endian byte order.
87    BigEndian,
88}
89
90impl ByteOrder {
91    /// Parses byte order from a string.
92    #[must_use]
93    pub fn parse(s: &str) -> Option<Self> {
94        match s.to_lowercase().as_str() {
95            "littleendian" | "little-endian" | "le" => Some(Self::LittleEndian),
96            "bigendian" | "big-endian" | "be" => Some(Self::BigEndian),
97            _ => None,
98        }
99    }
100}
101
102/// Type definition variants.
103#[derive(Debug, Clone)]
104pub enum TypeDef {
105    /// Primitive type definition.
106    Primitive(PrimitiveDef),
107    /// Composite type definition.
108    Composite(CompositeDef),
109    /// Enum type definition.
110    Enum(EnumDef),
111    /// Set (bitfield) type definition.
112    Set(SetDef),
113}
114
115impl TypeDef {
116    /// Returns the name of the type.
117    #[must_use]
118    pub fn name(&self) -> &str {
119        match self {
120            Self::Primitive(p) => &p.name,
121            Self::Composite(c) => &c.name,
122            Self::Enum(e) => &e.name,
123            Self::Set(s) => &s.name,
124        }
125    }
126
127    /// Returns the encoded size of the type in bytes.
128    #[must_use]
129    pub fn encoded_length(&self) -> usize {
130        match self {
131            Self::Primitive(p) => p.encoded_length(),
132            Self::Composite(c) => c.encoded_length(),
133            Self::Enum(e) => e.encoding_type.size(),
134            Self::Set(s) => s.encoding_type.size(),
135        }
136    }
137
138    /// Returns true if this is a primitive type.
139    #[must_use]
140    pub const fn is_primitive(&self) -> bool {
141        matches!(self, Self::Primitive(_))
142    }
143
144    /// Returns true if this is a composite type.
145    #[must_use]
146    pub const fn is_composite(&self) -> bool {
147        matches!(self, Self::Composite(_))
148    }
149
150    /// Returns true if this is an enum type.
151    #[must_use]
152    pub const fn is_enum(&self) -> bool {
153        matches!(self, Self::Enum(_))
154    }
155
156    /// Returns true if this is a set type.
157    #[must_use]
158    pub const fn is_set(&self) -> bool {
159        matches!(self, Self::Set(_))
160    }
161}
162
163/// Primitive type definition.
164#[derive(Debug, Clone)]
165pub struct PrimitiveDef {
166    /// Type name.
167    pub name: String,
168    /// Underlying primitive type.
169    pub primitive_type: PrimitiveType,
170    /// Array length (None for scalar).
171    pub length: Option<usize>,
172    /// Null value representation.
173    pub null_value: Option<String>,
174    /// Minimum valid value.
175    pub min_value: Option<String>,
176    /// Maximum valid value.
177    pub max_value: Option<String>,
178    /// Character encoding (for char arrays).
179    pub character_encoding: Option<String>,
180    /// Semantic type.
181    pub semantic_type: Option<String>,
182    /// Description.
183    pub description: Option<String>,
184    /// Constant value (if presence is constant).
185    pub constant_value: Option<String>,
186}
187
188impl PrimitiveDef {
189    /// Creates a new primitive type definition.
190    #[must_use]
191    pub fn new(name: String, primitive_type: PrimitiveType) -> Self {
192        Self {
193            name,
194            primitive_type,
195            length: None,
196            null_value: None,
197            min_value: None,
198            max_value: None,
199            character_encoding: None,
200            semantic_type: None,
201            description: None,
202            constant_value: None,
203        }
204    }
205
206    /// Returns the encoded length in bytes.
207    #[must_use]
208    pub fn encoded_length(&self) -> usize {
209        let base_size = self.primitive_type.size();
210        self.length.map_or(base_size, |len| base_size * len)
211    }
212
213    /// Returns true if this is an array type.
214    #[must_use]
215    pub fn is_array(&self) -> bool {
216        self.length.is_some() && self.length != Some(1)
217    }
218}
219
220/// SBE primitive types.
221#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
222pub enum PrimitiveType {
223    /// Single character (1 byte).
224    Char,
225    /// Signed 8-bit integer.
226    Int8,
227    /// Signed 16-bit integer.
228    Int16,
229    /// Signed 32-bit integer.
230    Int32,
231    /// Signed 64-bit integer.
232    Int64,
233    /// Unsigned 8-bit integer.
234    Uint8,
235    /// Unsigned 16-bit integer.
236    Uint16,
237    /// Unsigned 32-bit integer.
238    Uint32,
239    /// Unsigned 64-bit integer.
240    Uint64,
241    /// 32-bit floating point.
242    Float,
243    /// 64-bit floating point.
244    Double,
245}
246
247impl PrimitiveType {
248    /// Returns the size of the primitive type in bytes.
249    #[must_use]
250    pub const fn size(&self) -> usize {
251        match self {
252            Self::Char | Self::Int8 | Self::Uint8 => 1,
253            Self::Int16 | Self::Uint16 => 2,
254            Self::Int32 | Self::Uint32 | Self::Float => 4,
255            Self::Int64 | Self::Uint64 | Self::Double => 8,
256        }
257    }
258
259    /// Returns the Rust type name for this primitive.
260    #[must_use]
261    pub const fn rust_type(&self) -> &'static str {
262        match self {
263            Self::Char => "u8",
264            Self::Int8 => "i8",
265            Self::Int16 => "i16",
266            Self::Int32 => "i32",
267            Self::Int64 => "i64",
268            Self::Uint8 => "u8",
269            Self::Uint16 => "u16",
270            Self::Uint32 => "u32",
271            Self::Uint64 => "u64",
272            Self::Float => "f32",
273            Self::Double => "f64",
274        }
275    }
276
277    /// Returns the SBE type name.
278    #[must_use]
279    pub const fn sbe_name(&self) -> &'static str {
280        match self {
281            Self::Char => "char",
282            Self::Int8 => "int8",
283            Self::Int16 => "int16",
284            Self::Int32 => "int32",
285            Self::Int64 => "int64",
286            Self::Uint8 => "uint8",
287            Self::Uint16 => "uint16",
288            Self::Uint32 => "uint32",
289            Self::Uint64 => "uint64",
290            Self::Float => "float",
291            Self::Double => "double",
292        }
293    }
294
295    /// Parses a primitive type from its SBE name.
296    #[must_use]
297    pub fn from_sbe_name(name: &str) -> Option<Self> {
298        match name {
299            "char" => Some(Self::Char),
300            "int8" => Some(Self::Int8),
301            "int16" => Some(Self::Int16),
302            "int32" => Some(Self::Int32),
303            "int64" => Some(Self::Int64),
304            "uint8" => Some(Self::Uint8),
305            "uint16" => Some(Self::Uint16),
306            "uint32" => Some(Self::Uint32),
307            "uint64" => Some(Self::Uint64),
308            "float" => Some(Self::Float),
309            "double" => Some(Self::Double),
310            _ => None,
311        }
312    }
313
314    /// Returns true if this is a signed integer type.
315    #[must_use]
316    pub const fn is_signed(&self) -> bool {
317        matches!(self, Self::Int8 | Self::Int16 | Self::Int32 | Self::Int64)
318    }
319
320    /// Returns true if this is an unsigned integer type.
321    #[must_use]
322    pub const fn is_unsigned(&self) -> bool {
323        matches!(
324            self,
325            Self::Uint8 | Self::Uint16 | Self::Uint32 | Self::Uint64
326        )
327    }
328
329    /// Returns true if this is a floating point type.
330    #[must_use]
331    pub const fn is_float(&self) -> bool {
332        matches!(self, Self::Float | Self::Double)
333    }
334}
335
336/// Composite type definition.
337#[derive(Debug, Clone)]
338pub struct CompositeDef {
339    /// Type name.
340    pub name: String,
341    /// Fields within the composite.
342    pub fields: Vec<CompositeField>,
343    /// Description.
344    pub description: Option<String>,
345    /// Semantic type.
346    pub semantic_type: Option<String>,
347}
348
349impl CompositeDef {
350    /// Creates a new composite type definition.
351    #[must_use]
352    pub fn new(name: String) -> Self {
353        Self {
354            name,
355            fields: Vec::new(),
356            description: None,
357            semantic_type: None,
358        }
359    }
360
361    /// Returns the encoded length in bytes.
362    #[must_use]
363    pub fn encoded_length(&self) -> usize {
364        self.fields.iter().map(|f| f.encoded_length).sum()
365    }
366
367    /// Adds a field to the composite.
368    pub fn add_field(&mut self, field: CompositeField) {
369        self.fields.push(field);
370    }
371}
372
373/// Field within a composite type.
374#[derive(Debug, Clone)]
375pub struct CompositeField {
376    /// Field name.
377    pub name: String,
378    /// Type name (primitive or another type).
379    pub type_name: String,
380    /// Primitive type (if directly a primitive).
381    pub primitive_type: Option<PrimitiveType>,
382    /// Offset within the composite (optional, calculated if not specified).
383    pub offset: Option<usize>,
384    /// Encoded length in bytes.
385    pub encoded_length: usize,
386    /// Semantic type.
387    pub semantic_type: Option<String>,
388    /// Description.
389    pub description: Option<String>,
390    /// Constant value.
391    pub constant_value: Option<String>,
392}
393
394impl CompositeField {
395    /// Creates a new composite field.
396    #[must_use]
397    pub fn new(name: String, type_name: String, encoded_length: usize) -> Self {
398        Self {
399            name,
400            type_name,
401            primitive_type: None,
402            offset: None,
403            encoded_length,
404            semantic_type: None,
405            description: None,
406            constant_value: None,
407        }
408    }
409}
410
411/// Enum type definition.
412#[derive(Debug, Clone)]
413pub struct EnumDef {
414    /// Type name.
415    pub name: String,
416    /// Underlying encoding type.
417    pub encoding_type: PrimitiveType,
418    /// Valid values.
419    pub valid_values: Vec<EnumValue>,
420    /// Null value (for optional enums).
421    pub null_value: Option<String>,
422    /// Description.
423    pub description: Option<String>,
424}
425
426impl EnumDef {
427    /// Creates a new enum type definition.
428    #[must_use]
429    pub fn new(name: String, encoding_type: PrimitiveType) -> Self {
430        Self {
431            name,
432            encoding_type,
433            valid_values: Vec::new(),
434            null_value: None,
435            description: None,
436        }
437    }
438
439    /// Adds a valid value to the enum.
440    pub fn add_value(&mut self, value: EnumValue) {
441        self.valid_values.push(value);
442    }
443
444    /// Looks up a value by name.
445    #[must_use]
446    pub fn get_value(&self, name: &str) -> Option<&EnumValue> {
447        self.valid_values.iter().find(|v| v.name == name)
448    }
449}
450
451/// Enum valid value.
452#[derive(Debug, Clone)]
453pub struct EnumValue {
454    /// Value name.
455    pub name: String,
456    /// Encoded value (as string, parsed based on encoding type).
457    pub value: String,
458    /// Description.
459    pub description: Option<String>,
460    /// Since version.
461    pub since_version: Option<u16>,
462    /// Deprecated since version.
463    pub deprecated: Option<u16>,
464}
465
466impl EnumValue {
467    /// Creates a new enum value.
468    #[must_use]
469    pub fn new(name: String, value: String) -> Self {
470        Self {
471            name,
472            value,
473            description: None,
474            since_version: None,
475            deprecated: None,
476        }
477    }
478
479    /// Parses the value as a u64.
480    #[must_use]
481    pub fn as_u64(&self) -> Option<u64> {
482        self.value.parse().ok()
483    }
484
485    /// Parses the value as an i64.
486    #[must_use]
487    pub fn as_i64(&self) -> Option<i64> {
488        self.value.parse().ok()
489    }
490}
491
492/// Set (bitfield) type definition.
493#[derive(Debug, Clone)]
494pub struct SetDef {
495    /// Type name.
496    pub name: String,
497    /// Underlying encoding type.
498    pub encoding_type: PrimitiveType,
499    /// Bit choices.
500    pub choices: Vec<SetChoice>,
501    /// Description.
502    pub description: Option<String>,
503}
504
505impl SetDef {
506    /// Creates a new set type definition.
507    #[must_use]
508    pub fn new(name: String, encoding_type: PrimitiveType) -> Self {
509        Self {
510            name,
511            encoding_type,
512            choices: Vec::new(),
513            description: None,
514        }
515    }
516
517    /// Adds a choice to the set.
518    pub fn add_choice(&mut self, choice: SetChoice) {
519        self.choices.push(choice);
520    }
521
522    /// Looks up a choice by name.
523    #[must_use]
524    pub fn get_choice(&self, name: &str) -> Option<&SetChoice> {
525        self.choices.iter().find(|c| c.name == name)
526    }
527}
528
529/// Set choice (bit position).
530#[derive(Debug, Clone)]
531pub struct SetChoice {
532    /// Choice name.
533    pub name: String,
534    /// Bit position (0-based).
535    pub bit_position: u8,
536    /// Description.
537    pub description: Option<String>,
538    /// Since version.
539    pub since_version: Option<u16>,
540    /// Deprecated since version.
541    pub deprecated: Option<u16>,
542}
543
544impl SetChoice {
545    /// Creates a new set choice.
546    #[must_use]
547    pub fn new(name: String, bit_position: u8) -> Self {
548        Self {
549            name,
550            bit_position,
551            description: None,
552            since_version: None,
553            deprecated: None,
554        }
555    }
556
557    /// Returns the bit mask for this choice.
558    #[must_use]
559    pub const fn mask(&self) -> u64 {
560        1u64 << self.bit_position
561    }
562}
563
564/// Field presence indicator.
565#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
566pub enum Presence {
567    /// Field is required and must have a value.
568    #[default]
569    Required,
570    /// Field is optional and may be null.
571    Optional,
572    /// Field has a constant value defined in the schema.
573    Constant,
574}
575
576impl Presence {
577    /// Parses presence from a string.
578    #[must_use]
579    pub fn parse(s: &str) -> Option<Self> {
580        match s.to_lowercase().as_str() {
581            "required" => Some(Self::Required),
582            "optional" => Some(Self::Optional),
583            "constant" => Some(Self::Constant),
584            _ => None,
585        }
586    }
587}
588
589#[cfg(test)]
590mod tests {
591    use super::*;
592
593    #[test]
594    fn test_primitive_type_size() {
595        assert_eq!(PrimitiveType::Char.size(), 1);
596        assert_eq!(PrimitiveType::Int64.size(), 8);
597        assert_eq!(PrimitiveType::Double.size(), 8);
598    }
599
600    #[test]
601    fn test_primitive_def_encoded_length() {
602        let scalar = PrimitiveDef::new("price".to_string(), PrimitiveType::Int64);
603        assert_eq!(scalar.encoded_length(), 8);
604
605        let mut array = PrimitiveDef::new("symbol".to_string(), PrimitiveType::Char);
606        array.length = Some(8);
607        assert_eq!(array.encoded_length(), 8);
608    }
609
610    #[test]
611    fn test_composite_encoded_length() {
612        let mut composite = CompositeDef::new("decimal".to_string());
613        composite.add_field(CompositeField::new(
614            "mantissa".to_string(),
615            "int64".to_string(),
616            8,
617        ));
618        composite.add_field(CompositeField::new(
619            "exponent".to_string(),
620            "int8".to_string(),
621            1,
622        ));
623        assert_eq!(composite.encoded_length(), 9);
624    }
625
626    #[test]
627    fn test_enum_def() {
628        let mut enum_def = EnumDef::new("Side".to_string(), PrimitiveType::Uint8);
629        enum_def.add_value(EnumValue::new("Buy".to_string(), "1".to_string()));
630        enum_def.add_value(EnumValue::new("Sell".to_string(), "2".to_string()));
631
632        assert_eq!(enum_def.valid_values.len(), 2);
633        assert_eq!(enum_def.get_value("Buy").unwrap().as_u64(), Some(1));
634    }
635
636    #[test]
637    fn test_set_choice_mask() {
638        let choice = SetChoice::new("Flag1".to_string(), 0);
639        assert_eq!(choice.mask(), 1);
640
641        let choice2 = SetChoice::new("Flag8".to_string(), 7);
642        assert_eq!(choice2.mask(), 128);
643    }
644
645    #[test]
646    fn test_schema_type_lookup() {
647        let mut schema = Schema::new("test".to_string(), 1, 1);
648        schema.add_type(TypeDef::Primitive(PrimitiveDef::new(
649            "uint64".to_string(),
650            PrimitiveType::Uint64,
651        )));
652
653        assert!(schema.has_type("uint64"));
654        assert!(!schema.has_type("unknown"));
655        assert!(schema.get_type("uint64").is_some());
656    }
657
658    #[test]
659    fn test_schema_build_type_map() {
660        let mut schema = Schema::new("test".to_string(), 1, 1);
661        schema.types.push(TypeDef::Primitive(PrimitiveDef::new(
662            "int32".to_string(),
663            PrimitiveType::Int32,
664        )));
665        schema.types.push(TypeDef::Primitive(PrimitiveDef::new(
666            "int64".to_string(),
667            PrimitiveType::Int64,
668        )));
669
670        schema.build_type_map();
671
672        assert!(schema.has_type("int32"));
673        assert!(schema.has_type("int64"));
674    }
675
676    #[test]
677    fn test_byte_order_parse() {
678        assert_eq!(
679            ByteOrder::parse("littleEndian"),
680            Some(ByteOrder::LittleEndian)
681        );
682        assert_eq!(ByteOrder::parse("bigEndian"), Some(ByteOrder::BigEndian));
683        assert_eq!(ByteOrder::parse("le"), Some(ByteOrder::LittleEndian));
684        assert_eq!(ByteOrder::parse("be"), Some(ByteOrder::BigEndian));
685        assert_eq!(ByteOrder::parse("invalid"), None);
686    }
687
688    #[test]
689    fn test_presence_parse() {
690        assert_eq!(Presence::parse("required"), Some(Presence::Required));
691        assert_eq!(Presence::parse("optional"), Some(Presence::Optional));
692        assert_eq!(Presence::parse("constant"), Some(Presence::Constant));
693        assert_eq!(Presence::parse("REQUIRED"), Some(Presence::Required));
694        assert_eq!(Presence::parse("invalid"), None);
695    }
696
697    #[test]
698    fn test_primitive_type_rust_type() {
699        assert_eq!(PrimitiveType::Char.rust_type(), "u8");
700        assert_eq!(PrimitiveType::Int8.rust_type(), "i8");
701        assert_eq!(PrimitiveType::Uint64.rust_type(), "u64");
702        assert_eq!(PrimitiveType::Float.rust_type(), "f32");
703        assert_eq!(PrimitiveType::Double.rust_type(), "f64");
704    }
705
706    #[test]
707    fn test_primitive_type_sbe_name() {
708        assert_eq!(PrimitiveType::Char.sbe_name(), "char");
709        assert_eq!(PrimitiveType::Int32.sbe_name(), "int32");
710        assert_eq!(PrimitiveType::Uint64.sbe_name(), "uint64");
711    }
712
713    #[test]
714    fn test_primitive_type_from_sbe_name() {
715        assert_eq!(
716            PrimitiveType::from_sbe_name("char"),
717            Some(PrimitiveType::Char)
718        );
719        assert_eq!(
720            PrimitiveType::from_sbe_name("int64"),
721            Some(PrimitiveType::Int64)
722        );
723        assert_eq!(PrimitiveType::from_sbe_name("unknown"), None);
724    }
725
726    #[test]
727    fn test_type_def_name() {
728        let prim = TypeDef::Primitive(PrimitiveDef::new("test".to_string(), PrimitiveType::Int32));
729        assert_eq!(prim.name(), "test");
730
731        let comp = TypeDef::Composite(CompositeDef::new("decimal".to_string()));
732        assert_eq!(comp.name(), "decimal");
733
734        let enum_def = TypeDef::Enum(EnumDef::new("Side".to_string(), PrimitiveType::Uint8));
735        assert_eq!(enum_def.name(), "Side");
736
737        let set_def = TypeDef::Set(SetDef::new("Flags".to_string(), PrimitiveType::Uint8));
738        assert_eq!(set_def.name(), "Flags");
739    }
740
741    #[test]
742    fn test_enum_value_as_u64() {
743        let val = EnumValue::new("Buy".to_string(), "1".to_string());
744        assert_eq!(val.as_u64(), Some(1));
745
746        let val2 = EnumValue::new("Invalid".to_string(), "abc".to_string());
747        assert_eq!(val2.as_u64(), None);
748    }
749
750    #[test]
751    fn test_set_def() {
752        let mut set_def = SetDef::new("Flags".to_string(), PrimitiveType::Uint8);
753        set_def.add_choice(SetChoice::new("Active".to_string(), 0));
754        set_def.add_choice(SetChoice::new("Visible".to_string(), 1));
755
756        assert_eq!(set_def.choices.len(), 2);
757        assert_eq!(set_def.get_choice("Active").unwrap().bit_position, 0);
758        assert_eq!(set_def.get_choice("Visible").unwrap().bit_position, 1);
759    }
760
761    #[test]
762    fn test_composite_field() {
763        let field = CompositeField::new("mantissa".to_string(), "int64".to_string(), 8);
764        assert_eq!(field.name, "mantissa");
765        assert_eq!(field.type_name, "int64");
766        assert_eq!(field.encoded_length, 8);
767    }
768}