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