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