rusteal_codegen/rust_gen/
enums.rs1use crate::naming::escape_reserved;
4use crate::schema::EnumInfo;
5
6pub fn generate_enum(e: &EnumInfo) -> String {
8 let mut out = String::with_capacity(2048);
9
10 let name = &e.name;
11
12 let mut seen_values = std::collections::HashSet::new();
14 let mut seen_names = std::collections::HashSet::new();
15 let mut variants: Vec<(&str, i64)> = Vec::new();
16 for (variant_name, value) in &e.pairs {
17 if variant_name.ends_with("_MAX")
19 || variant_name == "MAX"
20 || variant_name.contains("__MAX")
21 {
22 continue;
23 }
24 if !seen_values.insert(*value) {
26 continue;
27 }
28 let clean = strip_enum_prefix(variant_name, &e.name);
30 let sanitized = sanitize_variant_name(clean);
31 if !seen_names.insert(sanitized) {
32 continue;
33 }
34 variants.push((variant_name, *value));
35 }
36
37 if variants.is_empty() {
38 let repr = underlying_to_repr(&e.underlying_type);
40 out.push_str(&format!(
41 "// Empty enum {name}\n\
42 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n\
43 pub struct {name}(pub {repr});\n"
44 ));
45 out.push_str(&format!(
46 "\nimpl rusteal_core::UeEnum for {name} {{\n type Repr = {repr};\n}}\n"
47 ));
48 generate_newtype_container_element(&mut out, name, repr);
49 return out;
50 }
51
52 let has_negative = variants.iter().any(|(_, v)| *v < 0);
56 let repr = if has_negative {
57 underlying_to_signed_repr(&e.underlying_type)
58 } else {
59 underlying_to_repr(&e.underlying_type)
60 };
61
62 if has_negative {
64 let mut seen_normalized = std::collections::HashSet::new();
65 variants.retain(|(_, v)| {
66 let normalized = normalize_to_signed(*v, &e.underlying_type);
67 seen_normalized.insert(normalized)
68 });
69 }
70
71 out.push_str(&format!(
73 "#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n\
74 #[repr({repr})]\n\
75 pub enum {name} {{\n"
76 ));
77
78 for (variant_name, value) in &variants {
79 let clean_name = strip_enum_prefix(variant_name, name);
80 let safe_name = escape_reserved(&sanitize_variant_name(clean_name));
81 let display_value = if has_negative {
82 normalize_to_signed(*value, &e.underlying_type)
83 } else {
84 *value
85 };
86 out.push_str(&format!(" {safe_name} = {display_value},\n"));
87 }
88
89 out.push_str("}\n");
90
91 out.push_str(&format!(
93 "\nimpl {name} {{\n pub fn from_value(v: {repr}) -> Option<Self> {{\n match v {{\n"
94 ));
95 for (variant_name, value) in &variants {
96 let clean_name = strip_enum_prefix(variant_name, name);
97 let safe_name = escape_reserved(&sanitize_variant_name(clean_name));
98 let display_value = if has_negative {
99 normalize_to_signed(*value, &e.underlying_type)
100 } else {
101 *value
102 };
103 out.push_str(&format!(" {display_value} => Some({name}::{safe_name}),\n"));
104 }
105 out.push_str(" _ => None,\n }\n }\n\n");
106
107 out.push_str(" pub fn display_name(&self) -> &'static str {\n match self {\n");
109 for (variant_name, _) in &variants {
110 let clean_name = strip_enum_prefix(variant_name, name);
111 let safe_name = escape_reserved(&sanitize_variant_name(clean_name));
112 out.push_str(&format!(
113 " {name}::{safe_name} => \"{clean_name}\",\n"
114 ));
115 }
116 out.push_str(" }\n }\n}\n");
117
118 out.push_str(&format!(
120 "\nimpl rusteal_core::UeEnum for {name} {{\n type Repr = {repr};\n}}\n"
121 ));
122
123 generate_enum_container_element(&mut out, name, repr);
125
126 out
127}
128
129fn underlying_to_repr(ut: &str) -> &'static str {
131 match ut {
132 "uint8" => "u8",
133 "int8" => "i8",
134 "uint16" => "u16",
135 "int16" => "i16",
136 "uint32" => "u32",
137 "int32" => "i32",
138 "uint64" => "u64",
139 "int64" => "i64",
140 _ => "u8",
141 }
142}
143
144fn underlying_to_signed_repr(ut: &str) -> &'static str {
146 match ut {
147 "uint8" => "i8",
148 "int8" => "i8",
149 "uint16" => "i16",
150 "int16" => "i16",
151 "uint32" => "i32",
152 "int32" => "i32",
153 "uint64" => "i64",
154 "int64" => "i64",
155 _ => "i8",
156 }
157}
158
159fn strip_enum_prefix<'a>(variant: &'a str, enum_name: &str) -> &'a str {
162 if let Some(rest) = variant.strip_prefix(enum_name)
163 && let Some(rest) = rest.strip_prefix("::")
164 {
165 return rest;
166 }
167 variant
168}
169
170fn normalize_to_signed(value: i64, underlying_type: &str) -> i64 {
173 match underlying_type {
174 "uint8" => (value as u8) as i8 as i64,
175 "int8" => (value as i8) as i64,
176 "uint16" => (value as u16) as i16 as i64,
177 "int16" => (value as i16) as i64,
178 "uint32" => (value as u32) as i32 as i64,
179 "int32" => (value as i32) as i64,
180 _ => value,
181 }
182}
183
184fn generate_enum_container_element(out: &mut String, name: &str, repr: &str) {
186 out.push_str(&format!(
187 "\nunsafe impl rusteal_core::ContainerElement for {name} {{\n\
188 \x20 const BUF_SIZE: u32 = std::mem::size_of::<{repr}>() as u32;\n\n\
189 \x20 #[inline]\n\
190 \x20 unsafe fn read_from_buf(buf: *const u8, _written: u32) -> Self {{\n\
191 \x20 unsafe {{ std::mem::transmute::<{repr}, Self>((buf as *const {repr}).read_unaligned()) }}\n\
192 \x20 }}\n\n\
193 \x20 #[inline]\n\
194 \x20 unsafe fn write_to_buf(&self, buf: *mut u8) -> u32 {{\n\
195 \x20 unsafe {{\n\
196 \x20 let raw: {repr} = std::mem::transmute(*self);\n\
197 \x20 (buf as *mut {repr}).write_unaligned(raw);\n\
198 \x20 }}\n\
199 \x20 std::mem::size_of::<{repr}>() as u32\n\
200 \x20 }}\n\
201 }}\n"
202 ));
203}
204
205fn generate_newtype_container_element(out: &mut String, name: &str, repr: &str) {
207 out.push_str(&format!(
208 "\nunsafe impl rusteal_core::ContainerElement for {name} {{\n\
209 \x20 const BUF_SIZE: u32 = std::mem::size_of::<{repr}>() as u32;\n\n\
210 \x20 #[inline]\n\
211 \x20 unsafe fn read_from_buf(buf: *const u8, _written: u32) -> Self {{\n\
212 \x20 unsafe {{ Self((buf as *const {repr}).read_unaligned()) }}\n\
213 \x20 }}\n\n\
214 \x20 #[inline]\n\
215 \x20 unsafe fn write_to_buf(&self, buf: *mut u8) -> u32 {{\n\
216 \x20 unsafe {{ (buf as *mut {repr}).write_unaligned(self.0) }};\n\
217 \x20 std::mem::size_of::<{repr}>() as u32\n\
218 \x20 }}\n\
219 }}\n"
220 ));
221}
222
223fn sanitize_variant_name(name: &str) -> String {
225 let mut result = String::with_capacity(name.len());
226 for (i, ch) in name.chars().enumerate() {
227 if ch.is_alphanumeric() || ch == '_' {
228 if i == 0 && ch.is_ascii_digit() {
229 result.push('_');
230 }
231 result.push(ch);
232 } else {
233 result.push('_');
234 }
235 }
236 if result.is_empty() {
237 result.push_str("_Unknown");
238 }
239 result
240}