1use std::iter;
2
3#[derive(Clone, Copy, Hash, PartialEq, Eq)]
4pub(crate) struct BoolSettingIndex(usize);
5
6#[derive(Hash, PartialEq, Eq)]
7pub(crate) struct BoolSetting {
8 pub default: bool,
9 pub bit_offset: u8,
10 pub predicate_number: u8,
11}
12
13#[derive(Hash, PartialEq, Eq)]
14pub(crate) enum SpecificSetting {
15 Bool(BoolSetting),
16 Enum(Vec<&'static str>),
17 Num(u8),
18}
19
20#[derive(Hash, PartialEq, Eq)]
21pub(crate) struct Setting {
22 pub name: &'static str,
23 pub description: &'static str,
24 pub comment: &'static str,
25 pub specific: SpecificSetting,
26 pub byte_offset: u8,
27}
28
29impl Setting {
30 pub fn default_byte(&self) -> u8 {
31 match self.specific {
32 SpecificSetting::Bool(BoolSetting {
33 default,
34 bit_offset,
35 ..
36 }) => {
37 if default {
38 1 << bit_offset
39 } else {
40 0
41 }
42 }
43 SpecificSetting::Enum(_) => 0,
44 SpecificSetting::Num(default) => default,
45 }
46 }
47
48 fn byte_for_value(&self, v: bool) -> u8 {
49 match self.specific {
50 SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => {
51 if v {
52 1 << bit_offset
53 } else {
54 0
55 }
56 }
57 _ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
58 }
59 }
60
61 fn byte_mask(&self) -> u8 {
62 match self.specific {
63 SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => 1 << bit_offset,
64 _ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
65 }
66 }
67}
68
69#[derive(Hash, PartialEq, Eq, Copy, Clone)]
70pub(crate) struct PresetIndex(usize);
71
72#[derive(Hash, PartialEq, Eq)]
73pub(crate) enum PresetType {
74 BoolSetting(BoolSettingIndex),
75 OtherPreset(PresetIndex),
76}
77
78impl From<BoolSettingIndex> for PresetType {
79 fn from(bool_setting_index: BoolSettingIndex) -> Self {
80 PresetType::BoolSetting(bool_setting_index)
81 }
82}
83impl From<PresetIndex> for PresetType {
84 fn from(value: PresetIndex) -> Self {
85 PresetType::OtherPreset(value)
86 }
87}
88
89#[derive(Hash, PartialEq, Eq)]
90pub(crate) struct Preset {
91 pub name: &'static str,
92 pub description: &'static str,
93 values: Vec<BoolSettingIndex>,
94}
95
96impl Preset {
97 pub fn layout(&self, group: &SettingGroup) -> Vec<(u8, u8)> {
98 let mut layout: Vec<(u8, u8)> = iter::repeat((0, 0))
99 .take(group.settings_size as usize)
100 .collect();
101 for bool_index in &self.values {
102 let setting = &group.settings[bool_index.0];
103 let mask = setting.byte_mask();
104 let val = setting.byte_for_value(true);
105 assert!((val & !mask) == 0);
106 let (ref mut l_mask, ref mut l_val) =
107 *layout.get_mut(setting.byte_offset as usize).unwrap();
108 *l_mask |= mask;
109 *l_val = (*l_val & !mask) | val;
110 }
111 layout
112 }
113
114 pub fn setting_names<'a>(
115 &'a self,
116 group: &'a SettingGroup,
117 ) -> impl Iterator<Item = &'static str> + 'a {
118 self.values
119 .iter()
120 .map(|bool_index| group.settings[bool_index.0].name)
121 }
122}
123
124pub(crate) struct SettingGroup {
125 pub name: &'static str,
126 pub settings: Vec<Setting>,
127 pub bool_start_byte_offset: u8,
128 pub settings_size: u8,
129 pub presets: Vec<Preset>,
130 pub predicates: Vec<Predicate>,
131}
132
133impl SettingGroup {
134 fn num_bool_settings(&self) -> u8 {
135 self.settings
136 .iter()
137 .filter(|s| matches!(s.specific, SpecificSetting::Bool(_)))
138 .count() as u8
139 }
140
141 pub fn byte_size(&self) -> u8 {
142 let num_predicates = self.num_bool_settings() + (self.predicates.len() as u8);
143 self.bool_start_byte_offset + (num_predicates + 7) / 8
144 }
145}
146
147pub(crate) enum ProtoSpecificSetting {
150 Bool(bool),
151 Enum(Vec<&'static str>),
152 Num(u8),
153}
154
155struct ProtoSetting {
157 name: &'static str,
158 description: &'static str,
159 comment: &'static str,
160 specific: ProtoSpecificSetting,
161}
162
163#[derive(Hash, PartialEq, Eq)]
164pub(crate) enum PredicateNode {
165 OwnedBool(BoolSettingIndex),
166 SharedBool(&'static str, &'static str),
167 And(Box<PredicateNode>, Box<PredicateNode>),
168}
169
170impl From<BoolSettingIndex> for PredicateNode {
171 fn from(bool_setting_index: BoolSettingIndex) -> Self {
172 PredicateNode::OwnedBool(bool_setting_index)
173 }
174}
175
176impl<'a> From<(BoolSettingIndex, &'a SettingGroup)> for PredicateNode {
177 fn from(val: (BoolSettingIndex, &'a SettingGroup)) -> Self {
178 let (index, group) = (val.0, val.1);
179 let setting = &group.settings[index.0];
180 PredicateNode::SharedBool(group.name, setting.name)
181 }
182}
183
184impl PredicateNode {
185 fn render(&self, group: &SettingGroup) -> String {
186 match *self {
187 PredicateNode::OwnedBool(bool_setting_index) => format!(
188 "{}.{}()",
189 group.name, group.settings[bool_setting_index.0].name
190 ),
191 PredicateNode::SharedBool(ref group_name, ref bool_name) => {
192 format!("{group_name}.{bool_name}()")
193 }
194 PredicateNode::And(ref lhs, ref rhs) => {
195 format!("{} && {}", lhs.render(group), rhs.render(group))
196 }
197 }
198 }
199}
200
201struct ProtoPredicate {
202 pub name: &'static str,
203 node: PredicateNode,
204}
205
206pub(crate) type SettingPredicateNumber = u8;
207
208pub(crate) struct Predicate {
209 pub name: &'static str,
210 node: PredicateNode,
211 pub number: SettingPredicateNumber,
212}
213
214impl Predicate {
215 pub fn render(&self, group: &SettingGroup) -> String {
216 self.node.render(group)
217 }
218}
219
220pub(crate) struct SettingGroupBuilder {
221 name: &'static str,
222 settings: Vec<ProtoSetting>,
223 presets: Vec<Preset>,
224 predicates: Vec<ProtoPredicate>,
225}
226
227impl SettingGroupBuilder {
228 pub fn new(name: &'static str) -> Self {
229 Self {
230 name,
231 settings: Vec::new(),
232 presets: Vec::new(),
233 predicates: Vec::new(),
234 }
235 }
236
237 fn add_setting(
238 &mut self,
239 name: &'static str,
240 description: &'static str,
241 comment: &'static str,
242 specific: ProtoSpecificSetting,
243 ) {
244 self.settings.push(ProtoSetting {
245 name,
246 description,
247 comment,
248 specific,
249 })
250 }
251
252 pub fn add_bool(
253 &mut self,
254 name: &'static str,
255 description: &'static str,
256 comment: &'static str,
257 default: bool,
258 ) -> BoolSettingIndex {
259 assert!(
260 self.predicates.is_empty(),
261 "predicates must be added after the boolean settings"
262 );
263 self.add_setting(
264 name,
265 description,
266 comment,
267 ProtoSpecificSetting::Bool(default),
268 );
269 BoolSettingIndex(self.settings.len() - 1)
270 }
271
272 pub fn add_enum(
273 &mut self,
274 name: &'static str,
275 description: &'static str,
276 comment: &'static str,
277 values: Vec<&'static str>,
278 ) {
279 self.add_setting(
280 name,
281 description,
282 comment,
283 ProtoSpecificSetting::Enum(values),
284 );
285 }
286
287 pub fn add_num(
288 &mut self,
289 name: &'static str,
290 description: &'static str,
291 comment: &'static str,
292 default: u8,
293 ) {
294 self.add_setting(
295 name,
296 description,
297 comment,
298 ProtoSpecificSetting::Num(default),
299 );
300 }
301
302 pub fn add_predicate(&mut self, name: &'static str, node: PredicateNode) {
303 self.predicates.push(ProtoPredicate { name, node });
304 }
305
306 pub fn add_preset(
307 &mut self,
308 name: &'static str,
309 description: &'static str,
310 args: Vec<PresetType>,
311 ) -> PresetIndex {
312 let mut values = Vec::new();
313 for arg in args {
314 match arg {
315 PresetType::OtherPreset(index) => {
316 values.extend(self.presets[index.0].values.iter());
317 }
318 PresetType::BoolSetting(index) => values.push(index),
319 }
320 }
321 self.presets.push(Preset {
322 name,
323 description,
324 values,
325 });
326 PresetIndex(self.presets.len() - 1)
327 }
328
329 pub fn build(self) -> SettingGroup {
349 let mut group = SettingGroup {
350 name: self.name,
351 settings: Vec::new(),
352 bool_start_byte_offset: 0,
353 settings_size: 0,
354 presets: Vec::new(),
355 predicates: Vec::new(),
356 };
357
358 let mut byte_offset = 0;
359
360 for s in &self.settings {
362 let specific = match s.specific {
363 ProtoSpecificSetting::Bool(..) => continue,
364 ProtoSpecificSetting::Enum(ref values) => SpecificSetting::Enum(values.clone()),
365 ProtoSpecificSetting::Num(default) => SpecificSetting::Num(default),
366 };
367
368 group.settings.push(Setting {
369 name: s.name,
370 description: s.description,
371 comment: s.comment,
372 byte_offset,
373 specific,
374 });
375
376 byte_offset += 1;
377 }
378
379 group.bool_start_byte_offset = byte_offset;
380
381 let mut predicate_number = 0;
382
383 for s in &self.settings {
385 let default = match s.specific {
386 ProtoSpecificSetting::Bool(default) => default,
387 ProtoSpecificSetting::Enum(_) | ProtoSpecificSetting::Num(_) => continue,
388 };
389 group.settings.push(Setting {
390 name: s.name,
391 description: s.description,
392 comment: s.comment,
393 byte_offset: byte_offset + predicate_number / 8,
394 specific: SpecificSetting::Bool(BoolSetting {
395 default,
396 bit_offset: predicate_number % 8,
397 predicate_number,
398 }),
399 });
400 predicate_number += 1;
401 }
402
403 assert!(
404 group.predicates.is_empty(),
405 "settings_size is the byte size before adding predicates"
406 );
407 group.settings_size = group.byte_size();
408
409 let mut predicates = self.predicates;
411 predicates.sort_by_key(|predicate| predicate.name);
412
413 group
414 .predicates
415 .extend(predicates.into_iter().map(|predicate| {
416 let number = predicate_number;
417 predicate_number += 1;
418 Predicate {
419 name: predicate.name,
420 node: predicate.node,
421 number,
422 }
423 }));
424
425 group.presets.extend(self.presets);
426
427 group
428 }
429}