Skip to main content

smart_keymap/keymap/
distinct_reports.rs

1use core::fmt::Debug;
2
3/// For tracking distinct HID reports from the keymap.
4#[cfg(feature = "std")]
5#[derive(Clone, Eq)]
6pub struct DistinctReports(Vec<[u8; 8]>);
7
8#[cfg(feature = "std")]
9struct ReportDebugHelper<'a>(&'a [u8; 8]);
10
11#[cfg(feature = "std")]
12impl Debug for ReportDebugHelper<'_> {
13    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
14        write!(
15            f,
16            "[{:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}]",
17            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7]
18        )
19    }
20}
21
22#[cfg(feature = "std")]
23impl Debug for DistinctReports {
24    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
25        // Debug fmt with each of the [u8; 8] on one line.
26        f.debug_tuple("DistinctReports")
27            .field(&self.0.iter().map(ReportDebugHelper).collect::<Vec<_>>())
28            .finish()
29    }
30}
31
32#[cfg(feature = "std")]
33impl Default for DistinctReports {
34    fn default() -> Self {
35        Self::new()
36    }
37}
38
39#[cfg(feature = "std")]
40impl core::cmp::PartialEq for DistinctReports {
41    fn eq(&self, other: &Self) -> bool {
42        // First element in DistinctReports should be [0; 8].
43        if self.0[0] != other.0[0] {
44            return false;
45        }
46
47        let mut i: usize = 1;
48        let mut j: usize = 1;
49
50        let self_len = self.0.len();
51        let other_len = other.0.len();
52
53        // Compare the rest of the elements.
54        while i < self_len && j < other_len {
55            // Ignore [0; 8] elements.
56            // (The reports are distinct; so, no two elements should be equal)
57            while (i < self_len - 1) && self.0[i] == [0; 8] {
58                i += 1;
59            }
60            while (j < other_len - 1) && other.0[j] == [0; 8] {
61                j += 1;
62            }
63
64            if self.0[i] != other.0[j] {
65                // Special cases
66                //  - comparing "modifier pressed" in two reports, vs one report.
67                //  - comparing "modifier released" in two reports, vs one report.
68                if i > 0
69                    && i < self_len - 1
70                    && self.0[i + 1] == other.0[j]
71                    && ((self.0[i - 1][0] == self.0[i][0] && self.0[i][2..] == self.0[i + 1][2..])
72                        || (self.0[i - 1][2..] == self.0[i][2..]
73                            && self.0[i][0] == self.0[i + 1][0]))
74                {
75                    // self uses two reports for equivalent of one report in other
76                    i += 1;
77                } else if j > 0
78                    && j < other_len - 1
79                    && self.0[i] == other.0[j + 1]
80                    && ((other.0[j - 1][0] == other.0[j][0]
81                        && other.0[j][2..] == other.0[j + 1][2..])
82                        || (other.0[j - 1][2..] == other.0[j][2..]
83                            && other.0[j][0] == other.0[j + 1][0]))
84                {
85                    // other uses two reports for equivalent of one report in self
86                    j += 1;
87                } else {
88                    return false;
89                }
90            }
91
92            i += 1;
93            j += 1;
94        }
95
96        i == self_len && j == other_len
97    }
98}
99
100#[cfg(feature = "std")]
101impl DistinctReports {
102    /// Constructs a new DistinctReports.
103    pub fn new() -> Self {
104        Self(vec![[0; 8]])
105    }
106
107    /// Adds the report to the distinct reports.
108    pub fn update(&mut self, report: [u8; 8]) {
109        match self.0.last() {
110            Some(last_report) if last_report == &report => {}
111            _ => self.0.push(report),
112        }
113    }
114
115    /// Access reports as slice of reports.
116    pub fn reports(&self) -> &[[u8; 8]] {
117        self.0.as_slice()
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn test_distinct_reports_equal() {
127        // Assemble
128        let lhs = DistinctReports(vec![[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0x04, 0, 0, 0, 0, 0]]);
129        let rhs = DistinctReports(vec![[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0x04, 0, 0, 0, 0, 0]]);
130
131        // Act
132
133        // Assert
134        assert!(lhs == rhs);
135    }
136
137    #[test]
138    fn test_distinct_reports_not_equal() {
139        // Assemble
140        let lhs = DistinctReports(vec![[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0x04, 0, 0, 0, 0, 0]]);
141        let rhs = DistinctReports(vec![[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0x05, 0, 0, 0, 0, 0]]);
142
143        // Act
144
145        // Assert
146        assert!(lhs != rhs);
147    }
148
149    #[test]
150    fn test_distinct_reports_not_equal_modif() {
151        // Assemble
152        let lhs = DistinctReports(vec![[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0x04, 0, 0, 0, 0, 0]]);
153        let rhs = DistinctReports(vec![
154            [0, 0, 0, 0, 0, 0, 0, 0],
155            [0x01, 0, 0x04, 0, 0, 0, 0, 0],
156        ]);
157
158        // Act
159
160        // Assert
161        assert!(lhs != rhs);
162    }
163
164    #[test]
165    fn test_distinct_reports_equal_ignores_0_between() {
166        // Assemble
167        let lhs = DistinctReports(vec![
168            [0, 0, 0, 0, 0, 0, 0, 0],
169            [0, 0, 0x04, 0, 0, 0, 0, 0],
170            [0, 0, 0x05, 0, 0, 0, 0, 0],
171        ]);
172        let rhs = DistinctReports(vec![
173            [0, 0, 0, 0, 0, 0, 0, 0],
174            [0, 0, 0x04, 0, 0, 0, 0, 0],
175            [0, 0, 0, 0, 0, 0, 0, 0],
176            [0, 0, 0x05, 0, 0, 0, 0, 0],
177        ]);
178
179        // Act
180
181        // Assert
182        assert!(lhs == rhs);
183    }
184
185    #[test]
186    fn test_distinct_reports_not_equal_respects_trailing_0() {
187        // Assemble
188        let lhs = DistinctReports(vec![
189            [0, 0, 0, 0, 0, 0, 0, 0],
190            [0, 0, 0x04, 0, 0, 0, 0, 0],
191            [0, 0, 0x05, 0, 0, 0, 0, 0],
192            [0, 0, 0, 0, 0, 0, 0, 0],
193        ]);
194        let rhs = DistinctReports(vec![
195            [0, 0, 0, 0, 0, 0, 0, 0],
196            [0, 0, 0x04, 0, 0, 0, 0, 0],
197            [0, 0, 0, 0, 0, 0, 0, 0],
198            [0, 0, 0x05, 0, 0, 0, 0, 0],
199        ]);
200
201        // Act
202
203        // Assert
204        assert!(lhs != rhs);
205    }
206
207    #[test]
208    fn test_distinct_reports_update_ignores_consecutive_duplicate() {
209        // Assemble
210        let lhs = DistinctReports(vec![[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0x04, 0, 0, 0, 0, 0]]);
211
212        // Act
213        let mut rhs = DistinctReports::new();
214        rhs.update([0, 0, 0x04, 0, 0, 0, 0, 0]);
215        rhs.update([0, 0, 0x04, 0, 0, 0, 0, 0]);
216        rhs.update([0, 0, 0x04, 0, 0, 0, 0, 0]);
217
218        // Assert
219        assert!(lhs == rhs);
220    }
221
222    #[test]
223    fn test_distinct_reports_allows_modifier_press_equivalence() {
224        // Assemble
225        let lhs = DistinctReports(vec![
226            [0, 0, 0, 0, 0, 0, 0, 0],
227            [0x01, 0, 0x04, 0, 0, 0, 0, 0],
228        ]);
229        let rhs = DistinctReports(vec![
230            [0, 0, 0, 0, 0, 0, 0, 0],
231            [0x01, 0, 0, 0, 0, 0, 0, 0],
232            [0x01, 0, 0x04, 0, 0, 0, 0, 0],
233        ]);
234
235        // Act
236
237        // Assert
238        assert!(lhs == rhs);
239    }
240
241    #[test]
242    fn test_distinct_reports_allows_modifier_release_equivalence() {
243        // Assemble
244        let lhs = DistinctReports(vec![
245            [0, 0, 0, 0, 0, 0, 0, 0],
246            [0x01, 0, 0x04, 0, 0, 0, 0, 0],
247            [0, 0, 0, 0, 0, 0, 0, 0],
248        ]);
249        let rhs = DistinctReports(vec![
250            [0, 0, 0, 0, 0, 0, 0, 0],
251            [0x01, 0, 0, 0, 0, 0, 0, 0],
252            [0x01, 0, 0x04, 0, 0, 0, 0, 0],
253            [0x01, 0, 0, 0, 0, 0, 0, 0],
254            [0, 0, 0, 0, 0, 0, 0, 0],
255        ]);
256
257        // Act
258
259        // Assert
260        assert!(lhs == rhs);
261    }
262}