Skip to main content

shuck_linter/
rule_set.rs

1use crate::Rule;
2
3const WORD_COUNT: usize = Rule::COUNT.div_ceil(64);
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct RuleSet([u64; WORD_COUNT]);
7
8impl RuleSet {
9    pub const EMPTY: Self = Self([0; WORD_COUNT]);
10
11    pub fn all() -> Self {
12        Rule::iter().collect()
13    }
14
15    pub const fn from_rules(rules: &[Rule]) -> Self {
16        let mut words = [0; WORD_COUNT];
17        let mut index = 0;
18        while index < rules.len() {
19            let rule = rules[index] as usize;
20            let word = rule / 64;
21            let bit = rule % 64;
22            words[word] |= 1u64 << bit;
23            index += 1;
24        }
25        Self(words)
26    }
27
28    pub const fn contains(&self, rule: Rule) -> bool {
29        let word = (rule as usize) / 64;
30        let bit = (rule as usize) % 64;
31        (self.0[word] & (1u64 << bit)) != 0
32    }
33
34    pub fn insert(&mut self, rule: Rule) {
35        let word = (rule as usize) / 64;
36        let bit = (rule as usize) % 64;
37        self.0[word] |= 1u64 << bit;
38    }
39
40    pub fn remove(&mut self, rule: Rule) {
41        let word = (rule as usize) / 64;
42        let bit = (rule as usize) % 64;
43        self.0[word] &= !(1u64 << bit);
44    }
45
46    pub fn set(&mut self, rule: Rule, enabled: bool) {
47        if enabled {
48            self.insert(rule);
49        } else {
50            self.remove(rule);
51        }
52    }
53
54    pub fn union(&self, other: &Self) -> Self {
55        let mut words = [0; WORD_COUNT];
56        let mut index = 0;
57        while index < WORD_COUNT {
58            words[index] = self.0[index] | other.0[index];
59            index += 1;
60        }
61        Self(words)
62    }
63
64    pub fn subtract(&self, other: &Self) -> Self {
65        let mut words = [0; WORD_COUNT];
66        let mut index = 0;
67        while index < WORD_COUNT {
68            words[index] = self.0[index] & !other.0[index];
69            index += 1;
70        }
71        Self(words)
72    }
73
74    pub fn iter(&self) -> impl Iterator<Item = Rule> + '_ {
75        Rule::iter().filter(|rule| self.contains(*rule))
76    }
77
78    pub fn len(&self) -> usize {
79        self.0.iter().map(|word| word.count_ones() as usize).sum()
80    }
81
82    pub fn is_empty(&self) -> bool {
83        self.0.iter().all(|word| *word == 0)
84    }
85}
86
87impl Default for RuleSet {
88    fn default() -> Self {
89        Self::EMPTY
90    }
91}
92
93impl FromIterator<Rule> for RuleSet {
94    fn from_iter<T: IntoIterator<Item = Rule>>(iter: T) -> Self {
95        let mut set = Self::EMPTY;
96        for rule in iter {
97            set.insert(rule);
98        }
99        set
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn supports_insert_remove_and_iteration() {
109        let mut set = RuleSet::EMPTY;
110        assert!(set.is_empty());
111
112        set.insert(Rule::UnusedAssignment);
113        assert!(set.contains(Rule::UnusedAssignment));
114        assert_eq!(set.len(), 1);
115        assert_eq!(set.iter().collect::<Vec<_>>(), vec![Rule::UnusedAssignment]);
116
117        set.remove(Rule::UnusedAssignment);
118        assert!(set.is_empty());
119    }
120
121    #[test]
122    fn can_be_built_in_const_context() {
123        const RULES: RuleSet = RuleSet::from_rules(&[Rule::UnusedAssignment, Rule::BareRead]);
124
125        assert_eq!(
126            RULES.iter().collect::<Vec<_>>(),
127            vec![Rule::UnusedAssignment, Rule::BareRead]
128        );
129    }
130}