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)]
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 Into<PresetType> for BoolSettingIndex {
79     fn into(self) -> PresetType {
80         PresetType::BoolSetting(self)
81     }
82 }
83 impl Into<PresetType> for PresetIndex {
84     fn into(self) -> PresetType {
85         PresetType::OtherPreset(self)
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 
115 pub(crate) struct SettingGroup {
116     pub name: &'static str,
117     pub settings: Vec<Setting>,
118     pub bool_start_byte_offset: u8,
119     pub settings_size: u8,
120     pub presets: Vec<Preset>,
121     pub predicates: Vec<Predicate>,
122 }
123 
124 impl SettingGroup {
125     fn num_bool_settings(&self) -> u8 {
126         self.settings
127             .iter()
128             .filter(|s| {
129                 if let SpecificSetting::Bool(_) = s.specific {
130                     true
131                 } else {
132                     false
133                 }
134             })
135             .count() as u8
136     }
137 
138     pub fn byte_size(&self) -> u8 {
139         let num_predicates = self.num_bool_settings() + (self.predicates.len() as u8);
140         self.bool_start_byte_offset + (num_predicates + 7) / 8
141     }
142 
143     pub fn get_bool(&self, name: &'static str) -> (BoolSettingIndex, &Self) {
144         for (i, s) in self.settings.iter().enumerate() {
145             if let SpecificSetting::Bool(_) = s.specific {
146                 if s.name == name {
147                     return (BoolSettingIndex(i), self);
148                 }
149             }
150         }
151         panic!("Should have found bool setting by name.");
152     }
153 }
154 
155 /// This is the basic information needed to track the specific parts of a setting when building
156 /// them.
157 pub(crate) enum ProtoSpecificSetting {
158     Bool(bool),
159     Enum(Vec<&'static str>),
160     Num(u8),
161 }
162 
163 /// This is the information provided during building for a setting.
164 struct ProtoSetting {
165     name: &'static str,
166     description: &'static str,
167     comment: &'static str,
168     specific: ProtoSpecificSetting,
169 }
170 
171 #[derive(Hash, PartialEq, Eq)]
172 pub(crate) enum PredicateNode {
173     OwnedBool(BoolSettingIndex),
174     SharedBool(&'static str, &'static str),
175     Not(Box<PredicateNode>),
176     And(Box<PredicateNode>, Box<PredicateNode>),
177 }
178 
179 impl Into<PredicateNode> for BoolSettingIndex {
180     fn into(self) -> PredicateNode {
181         PredicateNode::OwnedBool(self)
182     }
183 }
184 impl<'a> Into<PredicateNode> for (BoolSettingIndex, &'a SettingGroup) {
185     fn into(self) -> PredicateNode {
186         let (index, group) = (self.0, self.1);
187         let setting = &group.settings[index.0];
188         PredicateNode::SharedBool(group.name, setting.name)
189     }
190 }
191 
192 impl PredicateNode {
193     fn render(&self, group: &SettingGroup) -> String {
194         match *self {
195             PredicateNode::OwnedBool(bool_setting_index) => format!(
196                 "{}.{}()",
197                 group.name, group.settings[bool_setting_index.0].name
198             ),
199             PredicateNode::SharedBool(ref group_name, ref bool_name) => {
200                 format!("{}.{}()", group_name, bool_name)
201             }
202             PredicateNode::And(ref lhs, ref rhs) => {
203                 format!("{} && {}", lhs.render(group), rhs.render(group))
204             }
205             PredicateNode::Not(ref node) => format!("!({})", node.render(group)),
206         }
207     }
208 }
209 
210 struct ProtoPredicate {
211     pub name: &'static str,
212     node: PredicateNode,
213 }
214 
215 pub(crate) type SettingPredicateNumber = u8;
216 
217 pub(crate) struct Predicate {
218     pub name: &'static str,
219     node: PredicateNode,
220     pub number: SettingPredicateNumber,
221 }
222 
223 impl Predicate {
224     pub fn render(&self, group: &SettingGroup) -> String {
225         self.node.render(group)
226     }
227 }
228 
229 pub(crate) struct SettingGroupBuilder {
230     name: &'static str,
231     settings: Vec<ProtoSetting>,
232     presets: Vec<Preset>,
233     predicates: Vec<ProtoPredicate>,
234 }
235 
236 impl SettingGroupBuilder {
237     pub fn new(name: &'static str) -> Self {
238         Self {
239             name,
240             settings: Vec::new(),
241             presets: Vec::new(),
242             predicates: Vec::new(),
243         }
244     }
245 
246     fn add_setting(
247         &mut self,
248         name: &'static str,
249         description: &'static str,
250         comment: &'static str,
251         specific: ProtoSpecificSetting,
252     ) {
253         self.settings.push(ProtoSetting {
254             name,
255             description,
256             comment,
257             specific,
258         })
259     }
260 
261     pub fn add_bool(
262         &mut self,
263         name: &'static str,
264         description: &'static str,
265         comment: &'static str,
266         default: bool,
267     ) -> BoolSettingIndex {
268         assert!(
269             self.predicates.is_empty(),
270             "predicates must be added after the boolean settings"
271         );
272         self.add_setting(
273             name,
274             description,
275             comment,
276             ProtoSpecificSetting::Bool(default),
277         );
278         BoolSettingIndex(self.settings.len() - 1)
279     }
280 
281     pub fn add_enum(
282         &mut self,
283         name: &'static str,
284         description: &'static str,
285         comment: &'static str,
286         values: Vec<&'static str>,
287     ) {
288         self.add_setting(
289             name,
290             description,
291             comment,
292             ProtoSpecificSetting::Enum(values),
293         );
294     }
295 
296     pub fn add_num(
297         &mut self,
298         name: &'static str,
299         description: &'static str,
300         comment: &'static str,
301         default: u8,
302     ) {
303         self.add_setting(
304             name,
305             description,
306             comment,
307             ProtoSpecificSetting::Num(default),
308         );
309     }
310 
311     pub fn add_predicate(&mut self, name: &'static str, node: PredicateNode) {
312         self.predicates.push(ProtoPredicate { name, node });
313     }
314 
315     pub fn add_preset(
316         &mut self,
317         name: &'static str,
318         description: &'static str,
319         args: Vec<PresetType>,
320     ) -> PresetIndex {
321         let mut values = Vec::new();
322         for arg in args {
323             match arg {
324                 PresetType::OtherPreset(index) => {
325                     values.extend(self.presets[index.0].values.iter());
326                 }
327                 PresetType::BoolSetting(index) => values.push(index),
328             }
329         }
330         self.presets.push(Preset {
331             name,
332             description,
333             values,
334         });
335         PresetIndex(self.presets.len() - 1)
336     }
337 
338     /// Compute the layout of the byte vector used to represent this settings
339     /// group.
340     ///
341     /// The byte vector contains the following entries in order:
342     ///
343     /// 1. Byte-sized settings like `NumSetting` and `EnumSetting`.
344     /// 2. `BoolSetting` settings.
345     /// 3. Precomputed named predicates.
346     /// 4. Other numbered predicates, including parent predicates that need to be accessible by
347     ///    number.
348     ///
349     /// Set `self.settings_size` to the length of the byte vector prefix that
350     /// contains the settings. All bytes after that are computed, not
351     /// configured.
352     ///
353     /// Set `self.boolean_offset` to the beginning of the numbered predicates,
354     /// 2. in the list above.
355     ///
356     /// Assign `byte_offset` and `bit_offset` fields in all settings.
357     pub fn build(self) -> SettingGroup {
358         let mut group = SettingGroup {
359             name: self.name,
360             settings: Vec::new(),
361             bool_start_byte_offset: 0,
362             settings_size: 0,
363             presets: Vec::new(),
364             predicates: Vec::new(),
365         };
366 
367         let mut byte_offset = 0;
368 
369         // Assign the non-boolean settings first.
370         for s in &self.settings {
371             let specific = match s.specific {
372                 ProtoSpecificSetting::Bool(..) => continue,
373                 ProtoSpecificSetting::Enum(ref values) => SpecificSetting::Enum(values.clone()),
374                 ProtoSpecificSetting::Num(default) => SpecificSetting::Num(default),
375             };
376 
377             group.settings.push(Setting {
378                 name: s.name,
379                 description: s.description,
380                 comment: s.comment,
381                 byte_offset,
382                 specific,
383             });
384 
385             byte_offset += 1;
386         }
387 
388         group.bool_start_byte_offset = byte_offset;
389 
390         let mut predicate_number = 0;
391 
392         // Then the boolean settings.
393         for s in &self.settings {
394             let default = match s.specific {
395                 ProtoSpecificSetting::Bool(default) => default,
396                 ProtoSpecificSetting::Enum(_) | ProtoSpecificSetting::Num(_) => continue,
397             };
398             group.settings.push(Setting {
399                 name: s.name,
400                 description: s.description,
401                 comment: s.comment,
402                 byte_offset: byte_offset + predicate_number / 8,
403                 specific: SpecificSetting::Bool(BoolSetting {
404                     default,
405                     bit_offset: predicate_number % 8,
406                     predicate_number,
407                 }),
408             });
409             predicate_number += 1;
410         }
411 
412         assert!(
413             group.predicates.is_empty(),
414             "settings_size is the byte size before adding predicates"
415         );
416         group.settings_size = group.byte_size();
417 
418         // Sort predicates by name to ensure the same order as the Python code.
419         let mut predicates = self.predicates;
420         predicates.sort_by_key(|predicate| predicate.name);
421 
422         group
423             .predicates
424             .extend(predicates.into_iter().map(|predicate| {
425                 let number = predicate_number;
426                 predicate_number += 1;
427                 Predicate {
428                     name: predicate.name,
429                     node: predicate.node,
430                     number,
431                 }
432             }));
433 
434         group.presets.extend(self.presets);
435 
436         group
437     }
438 }
439