ergo_sbe/ir.rs
1//! Token intermediate representation for an SBE schema.
2//!
3//! After [`crate::parse`], you usually only need [`Ir`] via [`crate::Schema`].
4//! The flat [`Token`] stream (sbe-tool style) uses [`Signal`] brackets for
5//! messages, fields, composites, enums, sets, groups, and var-data; [`Encoding`]
6//! holds wire layout. [`crate::resolve_schema`] fills offsets and defaults.
7//!
8//! Most application code never inspects IR directly — use generated codecs.
9
10/// Byte order declared by the schema; applies to every primitive encoding.
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum ByteOrder {
13 /// Little-endian — the SBE default.
14 LittleEndian,
15 /// Big-endian.
16 BigEndian,
17}
18
19/// Structural role of a token in the IR stream.
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub enum Signal {
22 /// Opens a message definition.
23 BeginMessage,
24 /// Closes a message definition.
25 EndMessage,
26 /// Opens a field — a message field or a composite member.
27 BeginField,
28 /// Closes a field.
29 EndField,
30 /// Opens a composite type definition.
31 BeginComposite,
32 /// Closes a composite type definition.
33 EndComposite,
34 /// Opens an enum type definition.
35 BeginEnum,
36 /// Closes an enum type definition.
37 EndEnum,
38 /// Opens a bitset (choice/set) type definition.
39 BeginSet,
40 /// Closes a bitset (choice/set) type definition.
41 EndSet,
42 /// Opens a repeating group.
43 BeginGroup,
44 /// Closes a repeating group.
45 EndGroup,
46 /// Opens a variable-length data field.
47 BeginVarData,
48 /// Closes a variable-length data field.
49 EndVarData,
50 /// A leaf encoding token (primitive within a composite, enum, or set).
51 Encoding,
52}
53
54/// Field presence semantics.
55#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
56pub enum Presence {
57 /// Always present on the wire.
58 #[default]
59 Required,
60 /// May be absent; encoded as the type's null value.
61 Optional,
62 /// Not encoded — the value is fixed by the schema.
63 Constant,
64}
65
66/// SBE primitive wire types.
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68pub enum PrimitiveType {
69 /// Single ASCII byte.
70 Char,
71 /// Signed 8-bit integer.
72 Int8,
73 /// Unsigned 8-bit integer.
74 UInt8,
75 /// Signed 16-bit integer.
76 Int16,
77 /// Unsigned 16-bit integer.
78 UInt16,
79 /// Signed 32-bit integer.
80 Int32,
81 /// Unsigned 32-bit integer.
82 UInt32,
83 /// Signed 64-bit integer.
84 Int64,
85 /// Unsigned 64-bit integer.
86 UInt64,
87 /// IEEE-754 single-precision float.
88 Float,
89 /// IEEE-754 double-precision float.
90 Double,
91}
92
93impl PrimitiveType {
94 /// On-wire size in bytes.
95 #[must_use]
96 pub const fn size(self) -> usize {
97 match self {
98 Self::Char | Self::Int8 | Self::UInt8 => 1,
99 Self::Int16 | Self::UInt16 => 2,
100 Self::Int32 | Self::UInt32 | Self::Float => 4,
101 Self::Int64 | Self::UInt64 | Self::Double => 8,
102 }
103 }
104
105 /// Unsigned integer primitives plus `char` (one octet).
106 pub(crate) const fn is_unsigned_int(self) -> bool {
107 matches!(
108 self,
109 Self::Char | Self::UInt8 | Self::UInt16 | Self::UInt32 | Self::UInt64
110 )
111 }
112
113 /// Signed integer primitives.
114 pub(crate) const fn is_signed_int(self) -> bool {
115 matches!(self, Self::Int8 | Self::Int16 | Self::Int32 | Self::Int64)
116 }
117
118 /// Inclusive range for a signed integer primitive.
119 pub(crate) const fn signed_range(self) -> Option<(i64, i64)> {
120 match self {
121 Self::Int8 => Some((i8::MIN as i64, i8::MAX as i64)),
122 Self::Int16 => Some((i16::MIN as i64, i16::MAX as i64)),
123 Self::Int32 => Some((i32::MIN as i64, i32::MAX as i64)),
124 Self::Int64 => Some((i64::MIN, i64::MAX)),
125 _ => None,
126 }
127 }
128
129 /// Maximum inclusive value for an unsigned integer primitive (or `char`).
130 pub(crate) const fn unsigned_max(self) -> Option<u64> {
131 match self {
132 Self::Char | Self::UInt8 => Some(u8::MAX as u64),
133 Self::UInt16 => Some(u16::MAX as u64),
134 Self::UInt32 => Some(u32::MAX as u64),
135 Self::UInt64 => Some(u64::MAX),
136 _ => None,
137 }
138 }
139}
140
141/// How a token is encoded on the wire.
142#[derive(Clone, Debug, Default, PartialEq, Eq)]
143pub struct Encoding {
144 /// Primitive type for leaf tokens; `None` for structural tokens.
145 pub primitive_type: Option<PrimitiveType>,
146 /// Byte offset within the enclosing block; `None` when not declared.
147 pub offset: Option<usize>,
148 /// Presence of the value.
149 pub presence: Presence,
150 /// Schema version in which this token was introduced.
151 pub since_version: u16,
152 /// Null sentinel for optional fields; `None` when not applicable.
153 pub null_value: Option<u64>,
154 /// Character encoding for string fields (e.g. `"UTF-8"`, `"ASCII"`); `None` for non-string fields.
155 pub character_encoding: Option<String>,
156 /// SBE semantic type annotation (e.g. `"Price"`, `"Qty"`); `None` when not declared.
157 pub semantic_type: Option<String>,
158 /// Minimum valid value for the type; `None` when not declared.
159 pub min_value: Option<u64>,
160 /// Maximum valid value for the type; `None` when not declared.
161 pub max_value: Option<u64>,
162 /// Human-readable description from XML; `None` when absent.
163 pub description: Option<String>,
164 /// Constant value for `presence="constant"` fields; `None` otherwise.
165 pub constant_value: Option<String>,
166 /// Array length for fixed-size primitive arrays.
167 pub length: Option<usize>,
168 /// Epoch for timestamp encoding (e.g. "unix"); `None` when not declared.
169 pub epoch: Option<String>,
170 /// Time unit for timestamp encoding (e.g. "nanoseconds"); `None` when not declared.
171 pub time_unit: Option<String>,
172 /// Whether this token's wire size is variable; used for var-data composites.
173 pub is_variable_length: bool,
174 /// Whether this type or field is marked as deprecated in the schema.
175 pub deprecated: bool,
176}
177
178/// One token in the flat IR stream.
179#[derive(Clone, Debug, PartialEq, Eq)]
180pub struct Token {
181 /// SBE field/message/type id; `None` for structural tokens that don't carry an id.
182 pub id: Option<u16>,
183 /// Name of the declared entity (message, field, composite, …).
184 pub name: String,
185 /// Structural role.
186 pub signal: Signal,
187 /// Wire-encoding metadata. Populated on `BeginField`; default elsewhere.
188 pub encoding: Encoding,
189 /// Source span into the input XML text, for miette diagnostics.
190 /// `None` for synthetic tokens not originating from the schema source.
191 pub span: Option<std::ops::Range<usize>>,
192}
193
194/// The parsed schema IR: schema-level metadata plus the token stream.
195#[derive(Clone, Debug, PartialEq, Eq)]
196pub struct Ir {
197 /// SBE package name.
198 pub package: String,
199 /// SBE schema id.
200 pub id: u16,
201 /// SBE schema version.
202 pub version: u16,
203 /// Schema-declared byte order.
204 pub byte_order: ByteOrder,
205 /// Schema-level description from XML; `None` when absent.
206 pub description: Option<String>,
207 /// Semantic version string from XML; `None` when absent.
208 pub semantic_version: Option<String>,
209 /// Name of the composite type used as the message header (default `"messageHeader"`).
210 pub header_type: String,
211 /// Flat token stream.
212 pub tokens: Vec<Token>,
213}