ironsbe_codegen/rust/
enums.rs1use ironsbe_schema::ir::{SchemaIr, TypeKind, to_pascal_case};
4use ironsbe_schema::types::PrimitiveType;
5
6pub struct EnumGenerator<'a> {
8 ir: &'a SchemaIr,
9}
10
11impl<'a> EnumGenerator<'a> {
12 #[must_use]
14 pub fn new(ir: &'a SchemaIr) -> Self {
15 Self { ir }
16 }
17
18 #[must_use]
20 pub fn generate(&self) -> String {
21 let mut output = String::new();
22
23 for resolved_type in self.ir.types.values() {
24 match resolved_type.kind {
25 TypeKind::Enum(encoding) => {
26 output.push_str(&self.generate_enum(&resolved_type.name, encoding));
27 }
28 TypeKind::Set(encoding) => {
29 output.push_str(&self.generate_set(&resolved_type.name, encoding));
30 }
31 _ => {}
32 }
33 }
34
35 output
36 }
37
38 fn generate_enum(&self, name: &str, encoding: PrimitiveType) -> String {
40 let mut output = String::new();
41 let rust_name = to_pascal_case(name);
42 let rust_type = encoding.rust_type();
43
44 output.push_str(&format!("/// {} enum.\n", rust_name));
45 output.push_str("#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n");
46 output.push_str(&format!("#[repr({})]\n", rust_type));
47 output.push_str(&format!("pub enum {} {{\n", rust_name));
48
49 output.push_str(" // Values generated from schema\n");
52 output.push_str("}\n\n");
53
54 output.push_str(&format!("impl From<{}> for {} {{\n", rust_type, rust_name));
56 output.push_str(&format!(" fn from(value: {}) -> Self {{\n", rust_type));
57 output.push_str(" // Match implementation\n");
58 output.push_str(" unsafe { std::mem::transmute(value) }\n");
59 output.push_str(" }\n");
60 output.push_str("}\n\n");
61
62 output.push_str(&format!("impl From<{}> for {} {{\n", rust_name, rust_type));
63 output.push_str(&format!(" fn from(value: {}) -> Self {{\n", rust_name));
64 output.push_str(" value as Self\n");
65 output.push_str(" }\n");
66 output.push_str("}\n\n");
67
68 output
69 }
70
71 fn generate_set(&self, name: &str, encoding: PrimitiveType) -> String {
73 let mut output = String::new();
74 let rust_name = to_pascal_case(name);
75 let rust_type = encoding.rust_type();
76
77 output.push_str(&format!("/// {} bitfield set.\n", rust_name));
78 output.push_str("#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n");
79 output.push_str(&format!("pub struct {}({});\n\n", rust_name, rust_type));
80
81 output.push_str(&format!("impl {} {{\n", rust_name));
82 output.push_str(&format!(" /// Creates a new empty {}.\n", rust_name));
83 output.push_str(" #[must_use]\n");
84 output.push_str(" pub const fn new() -> Self {\n");
85 output.push_str(" Self(0)\n");
86 output.push_str(" }\n\n");
87
88 output.push_str(&format!(" /// Creates from raw {} value.\n", rust_type));
89 output.push_str(" #[must_use]\n");
90 output.push_str(&format!(
91 " pub const fn from_raw(value: {}) -> Self {{\n",
92 rust_type
93 ));
94 output.push_str(" Self(value)\n");
95 output.push_str(" }\n\n");
96
97 output.push_str(" /// Returns the raw value.\n");
98 output.push_str(" #[must_use]\n");
99 output.push_str(&format!(
100 " pub const fn raw(&self) -> {} {{\n",
101 rust_type
102 ));
103 output.push_str(" self.0\n");
104 output.push_str(" }\n\n");
105
106 output.push_str(" /// Checks if a bit is set.\n");
107 output.push_str(" #[must_use]\n");
108 output.push_str(" pub const fn is_set(&self, bit: u8) -> bool {\n");
109 output.push_str(" (self.0 >> bit) & 1 != 0\n");
110 output.push_str(" }\n\n");
111
112 output.push_str(" /// Sets a bit.\n");
113 output.push_str(" pub fn set(&mut self, bit: u8) {\n");
114 output.push_str(" self.0 |= 1 << bit;\n");
115 output.push_str(" }\n\n");
116
117 output.push_str(" /// Clears a bit.\n");
118 output.push_str(" pub fn clear(&mut self, bit: u8) {\n");
119 output.push_str(" self.0 &= !(1 << bit);\n");
120 output.push_str(" }\n");
121
122 output.push_str("}\n\n");
123
124 output
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use ironsbe_schema::ir::SchemaIr;
132 use ironsbe_schema::parser::parse_schema;
133
134 fn create_test_ir_with_enum() -> SchemaIr {
135 let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
136<sbe:messageSchema xmlns:sbe="http://fixprotocol.io/2016/sbe"
137 package="test" id="1" version="1" byteOrder="littleEndian">
138 <types>
139 <enum name="Side" encodingType="uint8">
140 <validValue name="Buy">1</validValue>
141 <validValue name="Sell">2</validValue>
142 </enum>
143 </types>
144 <sbe:message name="Test" id="1" blockLength="1">
145 <field name="side" id="1" type="Side" offset="0"/>
146 </sbe:message>
147</sbe:messageSchema>"#;
148
149 let schema = parse_schema(xml).expect("Failed to parse");
150 SchemaIr::from_schema(&schema)
151 }
152
153 fn create_test_ir_with_set() -> SchemaIr {
154 let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
155<sbe:messageSchema xmlns:sbe="http://fixprotocol.io/2016/sbe"
156 package="test" id="1" version="1" byteOrder="littleEndian">
157 <types>
158 <set name="Flags" encodingType="uint8">
159 <choice name="Active">0</choice>
160 <choice name="Visible">1</choice>
161 </set>
162 </types>
163 <sbe:message name="Test" id="1" blockLength="1">
164 <field name="flags" id="1" type="Flags" offset="0"/>
165 </sbe:message>
166</sbe:messageSchema>"#;
167
168 let schema = parse_schema(xml).expect("Failed to parse");
169 SchemaIr::from_schema(&schema)
170 }
171
172 #[test]
173 fn test_enum_generator_new() {
174 let ir = create_test_ir_with_enum();
175 let generator = EnumGenerator::new(&ir);
176 assert!(!generator.ir.types.is_empty());
177 }
178
179 #[test]
180 fn test_generate_enum() {
181 let ir = create_test_ir_with_enum();
182 let generator = EnumGenerator::new(&ir);
183 let output = generator.generate();
184
185 assert!(output.contains("enum"));
186 assert!(output.contains("impl From"));
187 }
188
189 #[test]
190 fn test_generate_set() {
191 let ir = create_test_ir_with_set();
192 let generator = EnumGenerator::new(&ir);
193 let output = generator.generate();
194
195 assert!(output.contains("struct"));
196 assert!(output.contains("is_set"));
197 assert!(output.contains("set"));
198 assert!(output.contains("clear"));
199 }
200
201 #[test]
202 fn test_generate_empty_ir() {
203 let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
204<sbe:messageSchema xmlns:sbe="http://fixprotocol.io/2016/sbe"
205 package="test" id="1" version="1" byteOrder="littleEndian">
206 <types>
207 <type name="uint64" primitiveType="uint64"/>
208 </types>
209 <sbe:message name="Test" id="1" blockLength="8">
210 <field name="value" id="1" type="uint64" offset="0"/>
211 </sbe:message>
212</sbe:messageSchema>"#;
213
214 let schema = parse_schema(xml).expect("Failed to parse");
215 let ir = SchemaIr::from_schema(&schema);
216 let generator = EnumGenerator::new(&ir);
217 let output = generator.generate();
218
219 assert!(!output.contains("enum Side"));
221 }
222}