1use crate::cdsl::camel_case;
4use crate::cdsl::settings::{BoolSetting, Preset, Setting, SettingGroup, SpecificSetting};
5use crate::constant_hash::generate_table;
6use crate::unique_table::UniqueSeqTable;
7use cranelift_codegen_shared::constant_hash::simple_hash;
8use cranelift_srcgen::{Formatter, Language, Match, error, fmtln};
9use std::collections::HashMap;
10
11pub(crate) enum ParentGroup {
12 None,
13 Shared,
14}
15
16fn gen_constructor(group: &SettingGroup, parent: ParentGroup, fmt: &mut Formatter) {
18 let args = match parent {
19 ParentGroup::None => "builder: Builder",
20 ParentGroup::Shared => "shared: &settings::Flags, builder: &Builder",
21 };
22 fmt.add_block("impl Flags", |fmt| {
23 fmt.doc_comment(format!("Create flags {} settings group.", group.name));
24 fmtln!(
25 fmt,
26 "#[allow(unused_variables, reason = \"generated code\")]"
27 );
28 fmt.add_block(&format!("pub fn new({args}) -> Self"), |fmt| {
29 fmtln!(fmt, "let bvec = builder.state_for(\"{}\");", group.name);
30 fmtln!(
31 fmt,
32 "let mut {} = Self {{ bytes: [0; {}] }};",
33 group.name,
34 group.byte_size()
35 );
36 fmtln!(
37 fmt,
38 "debug_assert_eq!(bvec.len(), {});",
39 group.settings_size
40 );
41 fmtln!(
42 fmt,
43 "{}.bytes[0..{}].copy_from_slice(&bvec);",
44 group.name,
45 group.settings_size
46 );
47
48 fmtln!(fmt, "{}", group.name);
49 });
50 });
51}
52
53fn gen_iterator(group: &SettingGroup, fmt: &mut Formatter) {
55 fmt.add_block("impl Flags",|fmt| {
56 fmt.doc_comment("Iterates the setting values.");
57 fmt.add_block("pub fn iter(&self) -> impl Iterator<Item = Value> + use<>",|fmt| {
58 fmtln!(fmt, "let mut bytes = [0; {}];", group.settings_size);
59 fmtln!(fmt, "bytes.copy_from_slice(&self.bytes[0..{}]);", group.settings_size);
60 fmt.add_block("DESCRIPTORS.iter().filter_map(move |d|", |fmt| {
61 fmt.add_block("let values = match &d.detail", |fmt| {
62 fmtln!(fmt, "detail::Detail::Preset => return None,");
63 fmtln!(fmt, "detail::Detail::Enum {{ last, enumerators }} => Some(TEMPLATE.enums(*last, *enumerators)),");
64 fmtln!(fmt, "_ => None");
65 });
66 fmtln!(fmt, ";");
67 fmtln!(fmt, "Some(Value {{ name: d.name, detail: d.detail, values, value: bytes[d.offset as usize] }})");
68 });
69 fmtln!(fmt, ")");
70 });
71 });
72}
73
74fn gen_enum_all(name: &str, values: &[&'static str], fmt: &mut Formatter) {
76 fmtln!(
77 fmt,
78 "/// Returns a slice with all possible [{}] values.",
79 name
80 );
81 fmt.add_block(&format!("pub fn all() -> &'static [{name}]"), |fmt| {
82 fmtln!(fmt, "&[");
83 fmt.indent(|fmt| {
84 for v in values.iter() {
85 fmtln!(fmt, "Self::{},", camel_case(v));
86 }
87 });
88 fmtln!(fmt, "]");
89 });
90}
91
92fn gen_to_and_from_str(name: &str, values: &[&'static str], fmt: &mut Formatter) {
94 fmt.add_block(&format!("impl fmt::Display for {name}"), |fmt| {
95 fmt.add_block(
96 "fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result",
97 |fmt| {
98 fmt.add_block("f.write_str(match *self", |fmt| {
99 for v in values.iter() {
100 fmtln!(fmt, "Self::{} => \"{}\",", camel_case(v), v);
101 }
102 });
103 fmtln!(fmt, ")");
104 },
105 );
106 });
107
108 fmt.add_block(&format!("impl core::str::FromStr for {name}"), |fmt| {
109 fmtln!(fmt, "type Err = ();");
110 fmt.add_block("fn from_str(s: &str) -> Result<Self, Self::Err>", |fmt| {
111 fmt.add_block("match s", |fmt| {
112 for v in values.iter() {
113 fmtln!(fmt, "\"{}\" => Ok(Self::{}),", v, camel_case(v));
114 }
115 fmtln!(fmt, "_ => Err(()),");
116 });
117 });
118 });
119}
120
121fn gen_enum_types(group: &SettingGroup, fmt: &mut Formatter) {
123 for setting in group.settings.iter() {
124 let values = match setting.specific {
125 SpecificSetting::Bool(_) | SpecificSetting::Num(_) => continue,
126 SpecificSetting::Enum(ref values) => values,
127 };
128 let name = camel_case(setting.name);
129
130 fmt.doc_comment(format!("Values for `{}.{}`.", group.name, setting.name));
131 fmtln!(fmt, "#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]");
132 fmt.add_block(&format!("pub enum {name}"), |fmt| {
133 for v in values.iter() {
134 fmt.doc_comment(format!("`{v}`."));
135 fmtln!(fmt, "{},", camel_case(v));
136 }
137 });
138
139 fmt.add_block(&format!("impl {name}"), |fmt| {
140 gen_enum_all(&name, values, fmt);
141 });
142
143 gen_to_and_from_str(&name, values, fmt);
144 }
145}
146
147fn gen_getter(setting: &Setting, fmt: &mut Formatter) {
149 fmt.doc_comment(format!("{}\n{}", setting.description, setting.comment));
150 match setting.specific {
151 SpecificSetting::Bool(BoolSetting {
152 predicate_number, ..
153 }) => {
154 fmt.add_block(&format!("pub fn {}(&self) -> bool", setting.name), |fmt| {
155 fmtln!(fmt, "self.numbered_predicate({})", predicate_number);
156 });
157 }
158 SpecificSetting::Enum(ref values) => {
159 let ty = camel_case(setting.name);
160 fmt.add_block(
161 &format!("pub fn {}(&self) -> {}", setting.name, ty),
162 |fmt| {
163 let mut m = Match::new(format!("self.bytes[{}]", setting.byte_offset));
164 for (i, v) in values.iter().enumerate() {
165 m.arm_no_fields(format!("{i}"), format!("{}::{}", ty, camel_case(v)));
166 }
167 m.arm_no_fields("_", "panic!(\"Invalid enum value\")");
168 fmt.add_match(m);
169 },
170 );
171 }
172 SpecificSetting::Num(_) => {
173 fmt.add_block(&format!("pub fn {}(&self) -> u8", setting.name), |fmt| {
174 fmtln!(fmt, "self.bytes[{}]", setting.byte_offset);
175 });
176 }
177 }
178}
179
180fn gen_getters(group: &SettingGroup, fmt: &mut Formatter) {
182 fmt.doc_comment("User-defined settings.");
183 fmtln!(fmt, "#[allow(dead_code, reason = \"generated code\")]");
184 fmt.add_block("impl Flags", |fmt| {
185 if !group.settings.is_empty() {
186 fmt.doc_comment("Dynamic numbered predicate getter.");
187 fmt.add_block("fn numbered_predicate(&self, p: usize) -> bool", |fmt| {
188 fmtln!(
189 fmt,
190 "self.bytes[{} + p / 8] & (1 << (p % 8)) != 0",
191 group.bool_start_byte_offset
192 );
193 });
194 }
195
196 for setting in &group.settings {
197 gen_getter(setting, fmt);
198 }
199 });
200}
201
202#[derive(Hash, PartialEq, Eq)]
203enum SettingOrPreset<'a> {
204 Setting(&'a Setting),
205 Preset(&'a Preset),
206}
207
208impl<'a> SettingOrPreset<'a> {
209 fn name(&self) -> &str {
210 match *self {
211 SettingOrPreset::Setting(s) => s.name,
212 SettingOrPreset::Preset(p) => p.name,
213 }
214 }
215}
216
217fn gen_descriptors(group: &SettingGroup, fmt: &mut Formatter) {
219 let mut enum_table = UniqueSeqTable::new();
220
221 let mut descriptor_index_map: HashMap<SettingOrPreset, usize> = HashMap::new();
222
223 fmtln!(
225 fmt,
226 "static DESCRIPTORS: [detail::Descriptor; {}] = [",
227 group.settings.len() + group.presets.len()
228 );
229 fmt.indent(|fmt| {
230 for (idx, setting) in group.settings.iter().enumerate() {
231 fmt.add_block("detail::Descriptor", |fmt| {
232 fmtln!(fmt, "name: \"{}\",", setting.name);
233 fmtln!(fmt, "description: \"{}\",", setting.description);
234 fmtln!(fmt, "offset: {},", setting.byte_offset);
235 match setting.specific {
236 SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => {
237 fmtln!(
238 fmt,
239 "detail: detail::Detail::Bool {{ bit: {} }},",
240 bit_offset
241 );
242 }
243 SpecificSetting::Enum(ref values) => {
244 let offset = enum_table.add(values);
245 fmtln!(
246 fmt,
247 "detail: detail::Detail::Enum {{ last: {}, enumerators: {} }},",
248 values.len() - 1,
249 offset
250 );
251 }
252 SpecificSetting::Num(_) => {
253 fmtln!(fmt, "detail: detail::Detail::Num,");
254 }
255 }
256
257 descriptor_index_map.insert(SettingOrPreset::Setting(setting), idx);
258 });
259 fmtln!(fmt, ",");
260 }
261
262 for (idx, preset) in group.presets.iter().enumerate() {
263 fmt.add_block("detail::Descriptor", |fmt| {
264 fmtln!(fmt, "name: \"{}\",", preset.name);
265 fmtln!(fmt, "description: \"{}\",", preset.description);
266 fmtln!(fmt, "offset: {},", (idx as u8) * group.settings_size);
267 fmtln!(fmt, "detail: detail::Detail::Preset,");
268 });
269 fmtln!(fmt, ",");
270
271 let whole_idx = idx + group.settings.len();
272 descriptor_index_map.insert(SettingOrPreset::Preset(preset), whole_idx);
273 }
274 });
275 fmtln!(fmt, "];");
276
277 fmtln!(fmt, "static ENUMERATORS: [&str; {}] = [", enum_table.len());
279 fmt.indent(|fmt| {
280 for enum_val in enum_table.iter() {
281 fmtln!(fmt, "\"{}\",", enum_val);
282 }
283 });
284 fmtln!(fmt, "];");
285
286 let mut hash_entries: Vec<SettingOrPreset> = Vec::new();
288 hash_entries.extend(group.settings.iter().map(SettingOrPreset::Setting));
289 hash_entries.extend(group.presets.iter().map(SettingOrPreset::Preset));
290
291 let hash_table = generate_table(hash_entries.iter(), hash_entries.len(), |entry| {
292 simple_hash(entry.name())
293 });
294 fmtln!(fmt, "static HASH_TABLE: [u16; {}] = [", hash_table.len());
295 fmt.indent(|fmt| {
296 for h in &hash_table {
297 match *h {
298 Some(setting_or_preset) => fmtln!(
299 fmt,
300 "{},",
301 &descriptor_index_map
302 .get(setting_or_preset)
303 .unwrap()
304 .to_string()
305 ),
306 None => fmtln!(fmt, "0xffff,"),
307 }
308 }
309 });
310 fmtln!(fmt, "];");
311
312 fmtln!(
314 fmt,
315 "static PRESETS: [(u8, u8); {}] = [",
316 group.presets.len() * (group.settings_size as usize)
317 );
318 fmt.indent(|fmt| {
319 for preset in &group.presets {
320 fmt.comment(format!(
321 "{}: {}",
322 preset.name,
323 preset.setting_names(group).collect::<Vec<_>>().join(", ")
324 ));
325 for (mask, value) in preset.layout(group) {
326 fmtln!(fmt, "(0b{:08b}, 0b{:08b}),", mask, value);
327 }
328 }
329 });
330 fmtln!(fmt, "];");
331}
332
333fn gen_template(group: &SettingGroup, fmt: &mut Formatter) {
334 let mut default_bytes: Vec<u8> = vec![0; group.settings_size as usize];
335 for setting in &group.settings {
336 *default_bytes.get_mut(setting.byte_offset as usize).unwrap() |= setting.default_byte();
337 }
338
339 let default_bytes: Vec<String> = default_bytes.iter().map(|x| format!("{x:#04x}")).collect();
340 let default_bytes_str = default_bytes.join(", ");
341
342 fmt.add_block(
343 "static TEMPLATE: detail::Template = detail::Template",
344 |fmt| {
345 fmtln!(fmt, "name: \"{}\",", group.name);
346 fmtln!(fmt, "descriptors: &DESCRIPTORS,");
347 fmtln!(fmt, "enumerators: &ENUMERATORS,");
348 fmtln!(fmt, "hash_table: &HASH_TABLE,");
349 fmtln!(fmt, "defaults: &[{}],", default_bytes_str);
350 fmtln!(fmt, "presets: &PRESETS,");
351 },
352 );
353 fmtln!(fmt, ";");
354
355 fmt.doc_comment(format!(
356 "Create a `settings::Builder` for the {} settings group.",
357 group.name
358 ));
359 fmt.add_block("pub fn builder() -> Builder", |fmt| {
360 fmtln!(fmt, "Builder::new(&TEMPLATE)");
361 });
362}
363
364fn gen_display(group: &SettingGroup, fmt: &mut Formatter) {
365 fmt.add_block("impl fmt::Display for Flags", |fmt| {
366 fmt.add_block(
367 "fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result",
368 |fmt| {
369 fmtln!(fmt, "writeln!(f, \"[{}]\")?;", group.name);
370 fmt.add_block("for d in &DESCRIPTORS", |fmt| {
371 fmt.add_block("if !d.detail.is_preset()", |fmt| {
372 fmtln!(fmt, "write!(f, \"{{}} = \", d.name)?;");
373 fmtln!(
374 fmt,
375 "TEMPLATE.format_toml_value(d.detail, self.bytes[d.offset as usize], f)?;",
376 );
377 fmtln!(fmt, "writeln!(f)?;");
378 });
379 });
380 fmtln!(fmt, "Ok(())");
381 },
382 );
383 });
384}
385
386fn gen_hash_key(fmt: &mut Formatter) {
387 fmt.add_block("impl Flags", |fmt| {
388 fmt.doc_comment("Get the flag values as raw bytes for hashing.");
389 fmt.add_block("pub fn hash_key(&self) -> &[u8]", |fmt| {
390 fmtln!(fmt, "&self.bytes");
391 });
392 });
393}
394
395fn gen_group(group: &SettingGroup, parent: ParentGroup, fmt: &mut Formatter) {
396 fmtln!(fmt, "#[derive(Clone, PartialEq, Hash)]");
398 fmt.doc_comment(format!("Flags group `{}`.", group.name));
399 fmt.add_block("pub struct Flags", |fmt| {
400 fmtln!(fmt, "bytes: [u8; {}],", group.byte_size());
401 });
402
403 gen_constructor(group, parent, fmt);
404 gen_iterator(group, fmt);
405 gen_enum_types(group, fmt);
406 gen_getters(group, fmt);
407 gen_descriptors(group, fmt);
408 gen_template(group, fmt);
409 gen_display(group, fmt);
410 gen_hash_key(fmt);
411}
412
413pub(crate) fn generate(
414 settings: &SettingGroup,
415 parent_group: ParentGroup,
416 filename: &str,
417 out_dir: &std::path::Path,
418) -> Result<(), error::Error> {
419 let mut fmt = Formatter::new(Language::Rust);
420 gen_group(settings, parent_group, &mut fmt);
421 fmt.write(filename, out_dir)?;
422 Ok(())
423}