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 { default_byte(&self) -> u830 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 byte_for_value(&self, v: bool) -> u848 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 byte_mask(&self) -> u861 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 { from(bool_setting_index: BoolSettingIndex) -> Self79 fn from(bool_setting_index: BoolSettingIndex) -> Self { 80 PresetType::BoolSetting(bool_setting_index) 81 } 82 } 83 impl From<PresetIndex> for PresetType { from(value: PresetIndex) -> Self84 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 { layout(&self, group: &SettingGroup) -> Vec<(u8, u8)>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 setting_names<'a>( &'a self, group: &'a SettingGroup, ) -> impl Iterator<Item = &'static str> + 'a114 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 } 131 132 impl SettingGroup { num_bool_settings(&self) -> u8133 fn num_bool_settings(&self) -> u8 { 134 self.settings 135 .iter() 136 .filter(|s| matches!(s.specific, SpecificSetting::Bool(_))) 137 .count() as u8 138 } 139 byte_size(&self) -> u8140 pub fn byte_size(&self) -> u8 { 141 self.bool_start_byte_offset + (self.num_bool_settings() + 7) / 8 142 } 143 } 144 145 /// This is the basic information needed to track the specific parts of a setting when building 146 /// them. 147 pub(crate) enum ProtoSpecificSetting { 148 Bool(bool), 149 Enum(Vec<&'static str>), 150 Num(u8), 151 } 152 153 /// This is the information provided during building for a setting. 154 struct ProtoSetting { 155 name: &'static str, 156 description: &'static str, 157 comment: &'static str, 158 specific: ProtoSpecificSetting, 159 } 160 161 pub(crate) struct SettingGroupBuilder { 162 name: &'static str, 163 settings: Vec<ProtoSetting>, 164 presets: Vec<Preset>, 165 } 166 167 impl SettingGroupBuilder { new(name: &'static str) -> Self168 pub fn new(name: &'static str) -> Self { 169 Self { 170 name, 171 settings: Vec::new(), 172 presets: Vec::new(), 173 } 174 } 175 add_setting( &mut self, name: &'static str, description: &'static str, comment: &'static str, specific: ProtoSpecificSetting, )176 fn add_setting( 177 &mut self, 178 name: &'static str, 179 description: &'static str, 180 comment: &'static str, 181 specific: ProtoSpecificSetting, 182 ) { 183 self.settings.push(ProtoSetting { 184 name, 185 description, 186 comment, 187 specific, 188 }) 189 } 190 add_bool( &mut self, name: &'static str, description: &'static str, comment: &'static str, default: bool, ) -> BoolSettingIndex191 pub fn add_bool( 192 &mut self, 193 name: &'static str, 194 description: &'static str, 195 comment: &'static str, 196 default: bool, 197 ) -> BoolSettingIndex { 198 self.add_setting( 199 name, 200 description, 201 comment, 202 ProtoSpecificSetting::Bool(default), 203 ); 204 BoolSettingIndex(self.settings.len() - 1) 205 } 206 add_enum( &mut self, name: &'static str, description: &'static str, comment: &'static str, values: Vec<&'static str>, )207 pub fn add_enum( 208 &mut self, 209 name: &'static str, 210 description: &'static str, 211 comment: &'static str, 212 values: Vec<&'static str>, 213 ) { 214 self.add_setting( 215 name, 216 description, 217 comment, 218 ProtoSpecificSetting::Enum(values), 219 ); 220 } 221 add_num( &mut self, name: &'static str, description: &'static str, comment: &'static str, default: u8, )222 pub fn add_num( 223 &mut self, 224 name: &'static str, 225 description: &'static str, 226 comment: &'static str, 227 default: u8, 228 ) { 229 self.add_setting( 230 name, 231 description, 232 comment, 233 ProtoSpecificSetting::Num(default), 234 ); 235 } 236 add_preset( &mut self, name: &'static str, description: &'static str, args: Vec<PresetType>, ) -> PresetIndex237 pub fn add_preset( 238 &mut self, 239 name: &'static str, 240 description: &'static str, 241 args: Vec<PresetType>, 242 ) -> PresetIndex { 243 let mut values = Vec::new(); 244 for arg in args { 245 match arg { 246 PresetType::OtherPreset(index) => { 247 values.extend(self.presets[index.0].values.iter()); 248 } 249 PresetType::BoolSetting(index) => values.push(index), 250 } 251 } 252 self.presets.push(Preset { 253 name, 254 description, 255 values, 256 }); 257 PresetIndex(self.presets.len() - 1) 258 } 259 260 /// Compute the layout of the byte vector used to represent this settings 261 /// group. 262 /// 263 /// The byte vector contains the following entries in order: 264 /// 265 /// 1. Byte-sized settings like `NumSetting` and `EnumSetting`. 266 /// 2. `BoolSetting` settings. 267 /// 268 /// Set `self.settings_size` to the length of the byte vector prefix that 269 /// contains the settings. All bytes after that are computed, not 270 /// configured. 271 /// 272 /// Set `self.boolean_offset` to the beginning of the numbered predicates, 273 /// 2. in the list above. 274 /// 275 /// Assign `byte_offset` and `bit_offset` fields in all settings. build(self) -> SettingGroup276 pub fn build(self) -> SettingGroup { 277 let mut group = SettingGroup { 278 name: self.name, 279 settings: Vec::new(), 280 bool_start_byte_offset: 0, 281 settings_size: 0, 282 presets: Vec::new(), 283 }; 284 285 let mut byte_offset = 0; 286 287 // Assign the non-boolean settings first. 288 for s in &self.settings { 289 let specific = match s.specific { 290 ProtoSpecificSetting::Bool(..) => continue, 291 ProtoSpecificSetting::Enum(ref values) => SpecificSetting::Enum(values.clone()), 292 ProtoSpecificSetting::Num(default) => SpecificSetting::Num(default), 293 }; 294 295 group.settings.push(Setting { 296 name: s.name, 297 description: s.description, 298 comment: s.comment, 299 byte_offset, 300 specific, 301 }); 302 303 byte_offset += 1; 304 } 305 306 group.bool_start_byte_offset = byte_offset; 307 308 let mut predicate_number = 0; 309 310 // Then the boolean settings. 311 for s in &self.settings { 312 let default = match s.specific { 313 ProtoSpecificSetting::Bool(default) => default, 314 ProtoSpecificSetting::Enum(_) | ProtoSpecificSetting::Num(_) => continue, 315 }; 316 group.settings.push(Setting { 317 name: s.name, 318 description: s.description, 319 comment: s.comment, 320 byte_offset: byte_offset + predicate_number / 8, 321 specific: SpecificSetting::Bool(BoolSetting { 322 default, 323 bit_offset: predicate_number % 8, 324 predicate_number, 325 }), 326 }); 327 predicate_number += 1; 328 } 329 330 group.settings_size = group.byte_size(); 331 332 group.presets.extend(self.presets); 333 334 group 335 } 336 } 337