1use std::collections::HashMap;
7
8#[derive(Debug, Clone)]
10pub struct Schema {
11 pub package: String,
13 pub id: u16,
15 pub version: u16,
17 pub semantic_version: String,
19 pub description: Option<String>,
21 pub byte_order: ByteOrder,
23 pub header_type: String,
25 pub types: Vec<TypeDef>,
27 pub messages: Vec<super::messages::MessageDef>,
29 type_map: HashMap<String, usize>,
31}
32
33impl Schema {
34 #[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 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 #[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 #[must_use]
67 pub fn has_type(&self, name: &str) -> bool {
68 self.type_map.contains_key(name)
69 }
70
71 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
82pub enum ByteOrder {
83 #[default]
85 LittleEndian,
86 BigEndian,
88}
89
90impl ByteOrder {
91 #[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#[derive(Debug, Clone)]
104pub enum TypeDef {
105 Primitive(PrimitiveDef),
107 Composite(CompositeDef),
109 Enum(EnumDef),
111 Set(SetDef),
113}
114
115impl TypeDef {
116 #[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 #[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 #[must_use]
140 pub const fn is_primitive(&self) -> bool {
141 matches!(self, Self::Primitive(_))
142 }
143
144 #[must_use]
146 pub const fn is_composite(&self) -> bool {
147 matches!(self, Self::Composite(_))
148 }
149
150 #[must_use]
152 pub const fn is_enum(&self) -> bool {
153 matches!(self, Self::Enum(_))
154 }
155
156 #[must_use]
158 pub const fn is_set(&self) -> bool {
159 matches!(self, Self::Set(_))
160 }
161}
162
163#[derive(Debug, Clone)]
165pub struct PrimitiveDef {
166 pub name: String,
168 pub primitive_type: PrimitiveType,
170 pub length: Option<usize>,
172 pub null_value: Option<String>,
174 pub min_value: Option<String>,
176 pub max_value: Option<String>,
178 pub character_encoding: Option<String>,
180 pub semantic_type: Option<String>,
182 pub description: Option<String>,
184 pub constant_value: Option<String>,
186}
187
188impl PrimitiveDef {
189 #[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 #[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 #[must_use]
215 pub fn is_array(&self) -> bool {
216 self.length.is_some() && self.length != Some(1)
217 }
218}
219
220#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
222pub enum PrimitiveType {
223 Char,
225 Int8,
227 Int16,
229 Int32,
231 Int64,
233 Uint8,
235 Uint16,
237 Uint32,
239 Uint64,
241 Float,
243 Double,
245}
246
247impl PrimitiveType {
248 #[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 #[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 #[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 #[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 #[must_use]
316 pub const fn is_signed(&self) -> bool {
317 matches!(self, Self::Int8 | Self::Int16 | Self::Int32 | Self::Int64)
318 }
319
320 #[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 #[must_use]
331 pub const fn is_float(&self) -> bool {
332 matches!(self, Self::Float | Self::Double)
333 }
334}
335
336#[derive(Debug, Clone)]
338pub struct CompositeDef {
339 pub name: String,
341 pub fields: Vec<CompositeField>,
343 pub description: Option<String>,
345 pub semantic_type: Option<String>,
347}
348
349impl CompositeDef {
350 #[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 #[must_use]
363 pub fn encoded_length(&self) -> usize {
364 self.fields.iter().map(|f| f.encoded_length).sum()
365 }
366
367 pub fn add_field(&mut self, field: CompositeField) {
369 self.fields.push(field);
370 }
371}
372
373#[derive(Debug, Clone)]
375pub struct CompositeField {
376 pub name: String,
378 pub type_name: String,
380 pub primitive_type: Option<PrimitiveType>,
382 pub offset: Option<usize>,
384 pub encoded_length: usize,
386 pub semantic_type: Option<String>,
388 pub description: Option<String>,
390 pub constant_value: Option<String>,
392}
393
394impl CompositeField {
395 #[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#[derive(Debug, Clone)]
413pub struct EnumDef {
414 pub name: String,
416 pub encoding_type: PrimitiveType,
418 pub valid_values: Vec<EnumValue>,
420 pub null_value: Option<String>,
422 pub description: Option<String>,
424}
425
426impl EnumDef {
427 #[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 pub fn add_value(&mut self, value: EnumValue) {
441 self.valid_values.push(value);
442 }
443
444 #[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#[derive(Debug, Clone)]
453pub struct EnumValue {
454 pub name: String,
456 pub value: String,
458 pub description: Option<String>,
460 pub since_version: Option<u16>,
462 pub deprecated: Option<u16>,
464}
465
466impl EnumValue {
467 #[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 #[must_use]
481 pub fn as_u64(&self) -> Option<u64> {
482 self.value.parse().ok()
483 }
484
485 #[must_use]
487 pub fn as_i64(&self) -> Option<i64> {
488 self.value.parse().ok()
489 }
490}
491
492#[derive(Debug, Clone)]
494pub struct SetDef {
495 pub name: String,
497 pub encoding_type: PrimitiveType,
499 pub choices: Vec<SetChoice>,
501 pub description: Option<String>,
503}
504
505impl SetDef {
506 #[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 pub fn add_choice(&mut self, choice: SetChoice) {
519 self.choices.push(choice);
520 }
521
522 #[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#[derive(Debug, Clone)]
531pub struct SetChoice {
532 pub name: String,
534 pub bit_position: u8,
536 pub description: Option<String>,
538 pub since_version: Option<u16>,
540 pub deprecated: Option<u16>,
542}
543
544impl SetChoice {
545 #[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 #[must_use]
559 pub const fn mask(&self) -> u64 {
560 1u64 << self.bit_position
561 }
562}
563
564#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
566pub enum Presence {
567 #[default]
569 Required,
570 Optional,
572 Constant,
574}
575
576impl Presence {
577 #[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}