1 use std::iter;
2 
3 #[derive(Clone, Copy, Hash, PartialEq, Eq)]
4 pub(crate) struct BoolSettingIndex(usize);
5 
6 #[derive(Hash, PartialEq, Eq)]
7 pub(crate) struct BoolSetting {
8     pub default: bool,
9     pub bit_offset: u8,
10     pub predicate_number: u8,
11 }
12 
13 #[derive(Hash, PartialEq, Eq)]
14 pub(crate) enum SpecificSetting {
15     Bool(BoolSetting),
16     Enum(Vec<&'static str>),
17     Num(u8),
18 }
19 
20 #[derive(Hash, PartialEq, Eq)]
21 pub(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 
29 impl 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)]
70 pub(crate) struct PresetIndex(usize);
71 
72 #[derive(Hash, PartialEq, Eq)]
73 pub(crate) enum PresetType {
74     BoolSetting(BoolSettingIndex),
75     OtherPreset(PresetIndex),
76 }
77 
78 impl From<BoolSettingIndex> for PresetType {
79     fn from(bool_setting_index: BoolSettingIndex) -> Self {
80         PresetType::BoolSetting(bool_setting_index)
81     }
82 }
83 impl From<PresetIndex> for PresetType {
84     fn from(value: PresetIndex) -> Self {
85         PresetType::OtherPreset(value)
86     }
87 }
88 
89 #[derive(Hash, PartialEq, Eq)]
90 pub(crate) struct Preset {
91     pub name: &'static str,
92     pub description: &'static str,
93     values: Vec<BoolSettingIndex>,
94 }
95 
96 impl 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 
124 pub(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 
133 impl 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 
147 /// This is the basic information needed to track the specific parts of a setting when building
148 /// them.
149 pub(crate) enum ProtoSpecificSetting {
150     Bool(bool),
151     Enum(Vec<&'static str>),
152     Num(u8),
153 }
154 
155 /// This is the information provided during building for a setting.
156 struct ProtoSetting {
157     name: &'static str,
158     description: &'static str,
159     comment: &'static str,
160     specific: ProtoSpecificSetting,
161 }
162 
163 #[derive(Hash, PartialEq, Eq)]
164 pub(crate) enum PredicateNode {
165     OwnedBool(BoolSettingIndex),
166     SharedBool(&'static str, &'static str),
167     And(Box<PredicateNode>, Box<PredicateNode>),
168 }
169 
170 impl From<BoolSettingIndex> for PredicateNode {
171     fn from(bool_setting_index: BoolSettingIndex) -> Self {
172         PredicateNode::OwnedBool(bool_setting_index)
173     }
174 }
175 
176 impl<'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 
184 impl 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 
201 struct ProtoPredicate {
202     pub name: &'static str,
203     node: PredicateNode,
204 }
205 
206 pub(crate) type SettingPredicateNumber = u8;
207 
208 pub(crate) struct Predicate {
209     pub name: &'static str,
210     node: PredicateNode,
211     pub number: SettingPredicateNumber,
212 }
213 
214 impl Predicate {
215     pub fn render(&self, group: &SettingGroup) -> String {
216         self.node.render(group)
217     }
218 }
219 
220 pub(crate) struct SettingGroupBuilder {
221     name: &'static str,
222     settings: Vec<ProtoSetting>,
223     presets: Vec<Preset>,
224     predicates: Vec<ProtoPredicate>,
225 }
226 
227 impl 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     /// Compute the layout of the byte vector used to represent this settings
330     /// group.
331     ///
332     /// The byte vector contains the following entries in order:
333     ///
334     /// 1. Byte-sized settings like `NumSetting` and `EnumSetting`.
335     /// 2. `BoolSetting` settings.
336     /// 3. Precomputed named predicates.
337     /// 4. Other numbered predicates, including parent predicates that need to be accessible by
338     ///    number.
339     ///
340     /// Set `self.settings_size` to the length of the byte vector prefix that
341     /// contains the settings. All bytes after that are computed, not
342     /// configured.
343     ///
344     /// Set `self.boolean_offset` to the beginning of the numbered predicates,
345     /// 2. in the list above.
346     ///
347     /// Assign `byte_offset` and `bit_offset` fields in all settings.
348     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         // Assign the non-boolean settings first.
361         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         // Then the boolean settings.
384         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         // Sort predicates by name to ensure the same order as the Python code.
410         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 }
430