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