hid_report/
lib.rs

1#![no_std]
2#![deny(missing_docs)]
3
4//! Parse USB HID report descriptors and pretty print them.
5//!
6//! # Example
7//!
8//! ```
9//! use hid_report::{parse, pretty_print};
10//!
11//! let bytes = [
12//!     0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x02, 0x19,
13//!     0x00, 0x2A, 0x3C, 0x02, 0x15, 0x00, 0x26, 0x3C, 0x02,
14//!     0x95, 0x01, 0x75, 0x10, 0x81, 0x00, 0xC0,
15//! ];
16//! let mut items = parse(bytes);
17//! assert_eq!(items.next().unwrap().to_string(), "Usage Page (Consumer)");
18//! assert_eq!(items.next().unwrap().to_string(), "Usage (Consumer Control)");
19//! assert_eq!(items.next().unwrap().to_string(), "Collection (Application)");
20//! assert_eq!(items.next().unwrap().to_string(), "Report ID (2)");
21//! assert_eq!(items.next().unwrap().to_string(), "Usage Minimum (Undefined)");
22//! assert_eq!(items.next().unwrap().to_string(), "Usage Maximum (AC Format)");
23//! assert_eq!(items.next().unwrap().to_string(), "Logical Minimum (0)");
24//! assert_eq!(items.next().unwrap().to_string(), "Logical Maximum (572)");
25//! assert_eq!(items.next().unwrap().to_string(), "Report Count (1)");
26//! assert_eq!(items.next().unwrap().to_string(), "Report Size (16)");
27//! assert_eq!(
28//!     items.next().unwrap().to_string(),
29//!     "Input (Data, Array, Absolute, No Wrap, Linear, Preferred State, No Null Position)"
30//! );
31//! assert_eq!(items.next().unwrap().to_string(), "End Collection");
32//! assert_eq!(items.next(), None);
33//!
34//! let items = parse(bytes).collect::<Vec<_>>();
35//!
36//! const EXPECTED: &str = indoc::indoc! {"
37//!     0x05, 0x0C        // Usage Page (Consumer)
38//!     0x09, 0x01        // Usage (Consumer Control)
39//!     0xA1, 0x01        //   Collection (Application)
40//!     0x85, 0x02        //   Report ID (2)
41//!     0x19, 0x00        //   Usage Minimum (Undefined)
42//!     0x2A, 0x3C, 0x02  //   Usage Maximum (AC Format)
43//!     0x15, 0x00        //   Logical Minimum (0)
44//!     0x26, 0x3C, 0x02  //   Logical Maximum (572)
45//!     0x95, 0x01        //   Report Count (1)
46//!     0x75, 0x10        //   Report Size (16)
47//!     0x81, 0x00        //   Input (Data, Array, Absolute, No Wrap, Linear, Preferred State, No Null Position)
48//!     0xC0              // End Collection"
49//! };
50//!
51//! assert_eq!(pretty_print(&items), EXPECTED);
52//! ```
53
54extern crate alloc;
55extern crate core as std;
56
57mod error;
58mod global_items;
59mod local_items;
60mod macros;
61mod main_items;
62mod privates;
63mod reserved;
64
65use alloc::{
66    format,
67    string::{String, ToString},
68    vec::Vec,
69};
70use std::fmt::Display;
71
72pub use error::*;
73pub use global_items::*;
74pub use local_items::*;
75pub use main_items::*;
76pub(crate) use privates::*;
77pub use reserved::*;
78
79/// Report items enumeration.
80///
81/// The HID Report descriptor is made up of items that provide information
82/// about the device.
83///
84/// All items contain a 1-byte prefix which denotes the basic type of the item. The
85/// HID class defines two basic formats for items:
86///
87/// * Short items: 1–5 bytes total length; used for the most commonly occurring
88///   items. A short item typically contains 1 or 0 bytes of optional data.
89/// * Long items: 3–258 bytes in length; used for items that require larger data
90///   structures for parts.
91///
92/// NOTE: No long item tags are defined, these tags are reserved for future use.
93///
94/// The short item format packs the item size, type, and tag into the first byte. The
95/// first byte may be followed by 0, 1, 2, or 4 optional data bytes depending on the
96/// size of the data.
97///
98/// | Bit -8 | Bit 7-4 | Bit 3-2 | Bit 1-0 |
99/// | --- | --- | --- | --- |
100/// | \[data] | bTag | bType | bSize |
101///
102/// bSize: Numeric expression specifying size of data:
103///
104/// | bSize | size of data |
105/// | --- | --- |
106/// | 0 | 0 bytes |
107/// | 1 | 1 byte |
108/// | 2 | 2 bytes |
109/// | 3 | 4 bytes |
110///
111/// bType: Numeric expression identifying type of item where:
112///
113/// | bType | type of item |
114/// | --- | --- |
115/// | 0 | Main |
116/// | 1 | Global |
117/// | 2 | Local |
118/// | 3 | Reserved |
119///
120/// bTag: Numeric expression specifying the function of the item.
121#[derive(Clone, Debug, PartialEq, Eq)]
122pub enum ReportItem {
123    /// An [Input] item.
124    Input(Input),
125    /// An [Output] item.
126    Output(Output),
127    /// A [Feature] item.
128    Feature(Feature),
129    /// A [Collection] item.
130    Collection(Collection),
131    /// An [EndCollection] item.
132    EndCollection(EndCollection),
133    /// A [UsagePage] item.
134    UsagePage(UsagePage),
135    /// A [LogicalMinimum] item.
136    LogicalMinimum(LogicalMinimum),
137    /// A [LogicalMaximum] item.
138    LogicalMaximum(LogicalMaximum),
139    /// A [PhysicalMinimum] item.
140    PhysicalMinimum(PhysicalMinimum),
141    /// A [PhysicalMaximum] item.
142    PhysicalMaximum(PhysicalMaximum),
143    /// A [UnitExponent] item.
144    UnitExponent(UnitExponent),
145    /// A [Unit] item.
146    Unit(Unit),
147    /// A [ReportSize] item.
148    ReportSize(ReportSize),
149    /// A [ReportId] item.
150    ReportId(ReportId),
151    /// A [ReportCount] item.
152    ReportCount(ReportCount),
153    /// A [Push] item.
154    Push(Push),
155    /// A [Pop] item.
156    Pop(Pop),
157    /// A [Usage] item.
158    Usage(Usage),
159    /// A [UsageMinimum] item.
160    UsageMinimum(UsageMinimum),
161    /// A [UsageMaximum] item.
162    UsageMaximum(UsageMaximum),
163    /// A [DesignatorIndex] item.
164    DesignatorIndex(DesignatorIndex),
165    /// A [DesignatorMinimum] item.
166    DesignatorMinimum(DesignatorMinimum),
167    /// A [DesignatorMaximum] item.
168    DesignatorMaximum(DesignatorMaximum),
169    /// A [StringIndex] item.
170    StringIndex(StringIndex),
171    /// A [StringMinimum] item.
172    StringMinimum(StringMinimum),
173    /// A [StringMaximum] item.
174    StringMaximum(StringMaximum),
175    /// A [Delimiter] item.
176    Delimiter(Delimiter),
177    /// A [Reserved] item.
178    Reserved(Reserved),
179}
180
181impl AsRef<[u8]> for ReportItem {
182    fn as_ref(&self) -> &[u8] {
183        match self {
184            ReportItem::Input(inner) => inner.as_ref(),
185            ReportItem::Output(inner) => inner.as_ref(),
186            ReportItem::Feature(inner) => inner.as_ref(),
187            ReportItem::Collection(inner) => inner.as_ref(),
188            ReportItem::EndCollection(inner) => inner.as_ref(),
189            ReportItem::UsagePage(inner) => inner.as_ref(),
190            ReportItem::LogicalMinimum(inner) => inner.as_ref(),
191            ReportItem::LogicalMaximum(inner) => inner.as_ref(),
192            ReportItem::PhysicalMinimum(inner) => inner.as_ref(),
193            ReportItem::PhysicalMaximum(inner) => inner.as_ref(),
194            ReportItem::UnitExponent(inner) => inner.as_ref(),
195            ReportItem::Unit(inner) => inner.as_ref(),
196            ReportItem::ReportSize(inner) => inner.as_ref(),
197            ReportItem::ReportId(inner) => inner.as_ref(),
198            ReportItem::ReportCount(inner) => inner.as_ref(),
199            ReportItem::Push(inner) => inner.as_ref(),
200            ReportItem::Pop(inner) => inner.as_ref(),
201            ReportItem::Usage(inner) => inner.as_ref(),
202            ReportItem::UsageMinimum(inner) => inner.as_ref(),
203            ReportItem::UsageMaximum(inner) => inner.as_ref(),
204            ReportItem::DesignatorIndex(inner) => inner.as_ref(),
205            ReportItem::DesignatorMinimum(inner) => inner.as_ref(),
206            ReportItem::DesignatorMaximum(inner) => inner.as_ref(),
207            ReportItem::StringIndex(inner) => inner.as_ref(),
208            ReportItem::StringMinimum(inner) => inner.as_ref(),
209            ReportItem::StringMaximum(inner) => inner.as_ref(),
210            ReportItem::Delimiter(inner) => inner.as_ref(),
211            ReportItem::Reserved(inner) => inner.as_ref(),
212        }
213    }
214}
215
216impl Display for ReportItem {
217    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218        match self {
219            ReportItem::Input(inner) => inner.fmt(f),
220            ReportItem::Output(inner) => inner.fmt(f),
221            ReportItem::Feature(inner) => inner.fmt(f),
222            ReportItem::Collection(inner) => inner.fmt(f),
223            ReportItem::EndCollection(inner) => inner.fmt(f),
224            ReportItem::UsagePage(inner) => inner.fmt(f),
225            ReportItem::LogicalMinimum(inner) => inner.fmt(f),
226            ReportItem::LogicalMaximum(inner) => inner.fmt(f),
227            ReportItem::PhysicalMinimum(inner) => inner.fmt(f),
228            ReportItem::PhysicalMaximum(inner) => inner.fmt(f),
229            ReportItem::UnitExponent(inner) => inner.fmt(f),
230            ReportItem::Unit(inner) => inner.fmt(f),
231            ReportItem::ReportSize(inner) => inner.fmt(f),
232            ReportItem::ReportId(inner) => inner.fmt(f),
233            ReportItem::ReportCount(inner) => inner.fmt(f),
234            ReportItem::Push(inner) => inner.fmt(f),
235            ReportItem::Pop(inner) => inner.fmt(f),
236            ReportItem::Usage(inner) => inner.fmt(f),
237            ReportItem::UsageMinimum(inner) => inner.fmt(f),
238            ReportItem::UsageMaximum(inner) => inner.fmt(f),
239            ReportItem::DesignatorIndex(inner) => inner.fmt(f),
240            ReportItem::DesignatorMinimum(inner) => inner.fmt(f),
241            ReportItem::DesignatorMaximum(inner) => inner.fmt(f),
242            ReportItem::StringIndex(inner) => inner.fmt(f),
243            ReportItem::StringMinimum(inner) => inner.fmt(f),
244            ReportItem::StringMaximum(inner) => inner.fmt(f),
245            ReportItem::Delimiter(inner) => inner.fmt(f),
246            ReportItem::Reserved(inner) => inner.fmt(f),
247        }
248    }
249}
250
251impl ReportItem {
252    /// Create a new item from raw byte stream.
253    ///
254    /// Items that cannot be recognized will be treated as [`Reserved`](ReportItem::Reserved).
255    /// If you want to fail on unknown items, use [`new_strict()`](ReportItem::new_strict()) instead.
256    ///
257    /// # Example
258    ///
259    /// ```
260    /// use hid_report::ReportItem;
261    ///
262    /// let raw = [0x26, 0x3c, 0x02];
263    /// let item = ReportItem::new(&raw).unwrap();
264    /// assert!(matches!(item, ReportItem::LogicalMaximum(_)));
265    /// assert_eq!(item.to_string(), "Logical Maximum (572)");
266    /// ```
267    pub fn new(raw: &[u8]) -> Result<Self, HidError> {
268        if raw.is_empty() {
269            return Err(HidError::EmptyRawInput);
270        };
271        let expected = __data_size(raw[0]);
272        if expected + 1 != raw.len() {
273            return Err(HidError::DataSizeNotMatch {
274                expected,
275                provided: raw.len() - 1,
276            });
277        };
278        unsafe {
279            Ok(match raw[0] & 0b1111_1100 {
280                Input::PREFIX => ReportItem::Input(Input::new_unchecked(raw)),
281                Output::PREFIX => ReportItem::Output(Output::new_unchecked(raw)),
282                Feature::PREFIX => ReportItem::Feature(Feature::new_unchecked(raw)),
283                Collection::PREFIX => ReportItem::Collection(Collection::new_unchecked(raw)),
284                EndCollection::PREFIX => {
285                    ReportItem::EndCollection(EndCollection::new_unchecked(raw))
286                }
287                UsagePage::PREFIX => ReportItem::UsagePage(UsagePage::new_unchecked(raw)),
288                LogicalMinimum::PREFIX => {
289                    ReportItem::LogicalMinimum(LogicalMinimum::new_unchecked(raw))
290                }
291                LogicalMaximum::PREFIX => {
292                    ReportItem::LogicalMaximum(LogicalMaximum::new_unchecked(raw))
293                }
294                PhysicalMinimum::PREFIX => {
295                    ReportItem::PhysicalMinimum(PhysicalMinimum::new_unchecked(raw))
296                }
297                PhysicalMaximum::PREFIX => {
298                    ReportItem::PhysicalMaximum(PhysicalMaximum::new_unchecked(raw))
299                }
300                UnitExponent::PREFIX => ReportItem::UnitExponent(UnitExponent::new_unchecked(raw)),
301                Unit::PREFIX => ReportItem::Unit(Unit::new_unchecked(raw)),
302                ReportSize::PREFIX => ReportItem::ReportSize(ReportSize::new_unchecked(raw)),
303                ReportId::PREFIX => ReportItem::ReportId(ReportId::new_unchecked(raw)),
304                ReportCount::PREFIX => ReportItem::ReportCount(ReportCount::new_unchecked(raw)),
305                Push::PREFIX => ReportItem::Push(Push::new_unchecked(raw)),
306                Pop::PREFIX => ReportItem::Pop(Pop::new_unchecked(raw)),
307                Usage::PREFIX => ReportItem::Usage(Usage::new_unchecked(raw)),
308                UsageMinimum::PREFIX => ReportItem::UsageMinimum(UsageMinimum::new_unchecked(raw)),
309                UsageMaximum::PREFIX => ReportItem::UsageMaximum(UsageMaximum::new_unchecked(raw)),
310                DesignatorIndex::PREFIX => {
311                    ReportItem::DesignatorIndex(DesignatorIndex::new_unchecked(raw))
312                }
313                DesignatorMinimum::PREFIX => {
314                    ReportItem::DesignatorMinimum(DesignatorMinimum::new_unchecked(raw))
315                }
316                DesignatorMaximum::PREFIX => {
317                    ReportItem::DesignatorMaximum(DesignatorMaximum::new_unchecked(raw))
318                }
319                StringIndex::PREFIX => ReportItem::StringIndex(StringIndex::new_unchecked(raw)),
320                StringMinimum::PREFIX => {
321                    ReportItem::StringMinimum(StringMinimum::new_unchecked(raw))
322                }
323                StringMaximum::PREFIX => {
324                    ReportItem::StringMaximum(StringMaximum::new_unchecked(raw))
325                }
326                Delimiter::PREFIX => ReportItem::Delimiter(Delimiter::new_unchecked(raw)),
327                _ => ReportItem::Reserved(Reserved::new_unchecked(raw)),
328            })
329        }
330    }
331
332    /// Create a new item from raw byte stream in strict mode.
333    ///
334    /// Items that cannot be recognized will be treated as [`HidError::ReservedItem`].
335    pub fn new_strict(raw: &[u8]) -> Result<Self, crate::HidError> {
336        if raw.is_empty() {
337            return Err(crate::HidError::EmptyRawInput);
338        };
339        let expected = __data_size(raw[0]);
340        if expected + 1 != raw.len() {
341            return Err(HidError::DataSizeNotMatch {
342                expected,
343                provided: raw.len() - 1,
344            });
345        };
346        unsafe {
347            Ok(match raw[0] & 0b1111_1100 {
348                Input::PREFIX => ReportItem::Input(Input::new_unchecked(raw)),
349                Output::PREFIX => ReportItem::Output(Output::new_unchecked(raw)),
350                Feature::PREFIX => ReportItem::Feature(Feature::new_unchecked(raw)),
351                Collection::PREFIX => ReportItem::Collection(Collection::new_unchecked(raw)),
352                EndCollection::PREFIX => {
353                    ReportItem::EndCollection(EndCollection::new_unchecked(raw))
354                }
355                UsagePage::PREFIX => ReportItem::UsagePage(UsagePage::new_unchecked(raw)),
356                LogicalMinimum::PREFIX => {
357                    ReportItem::LogicalMinimum(LogicalMinimum::new_unchecked(raw))
358                }
359                LogicalMaximum::PREFIX => {
360                    ReportItem::LogicalMaximum(LogicalMaximum::new_unchecked(raw))
361                }
362                PhysicalMinimum::PREFIX => {
363                    ReportItem::PhysicalMinimum(PhysicalMinimum::new_unchecked(raw))
364                }
365                PhysicalMaximum::PREFIX => {
366                    ReportItem::PhysicalMaximum(PhysicalMaximum::new_unchecked(raw))
367                }
368                UnitExponent::PREFIX => ReportItem::UnitExponent(UnitExponent::new_unchecked(raw)),
369                Unit::PREFIX => ReportItem::Unit(Unit::new_unchecked(raw)),
370                ReportSize::PREFIX => ReportItem::ReportSize(ReportSize::new_unchecked(raw)),
371                ReportId::PREFIX => ReportItem::ReportId(ReportId::new_unchecked(raw)),
372                ReportCount::PREFIX => ReportItem::ReportCount(ReportCount::new_unchecked(raw)),
373                Push::PREFIX => ReportItem::Push(Push::new_unchecked(raw)),
374                Pop::PREFIX => ReportItem::Pop(Pop::new_unchecked(raw)),
375                Usage::PREFIX => ReportItem::Usage(Usage::new_unchecked(raw)),
376                UsageMinimum::PREFIX => ReportItem::UsageMinimum(UsageMinimum::new_unchecked(raw)),
377                UsageMaximum::PREFIX => ReportItem::UsageMaximum(UsageMaximum::new_unchecked(raw)),
378                DesignatorIndex::PREFIX => {
379                    ReportItem::DesignatorIndex(DesignatorIndex::new_unchecked(raw))
380                }
381                DesignatorMinimum::PREFIX => {
382                    ReportItem::DesignatorMinimum(DesignatorMinimum::new_unchecked(raw))
383                }
384                DesignatorMaximum::PREFIX => {
385                    ReportItem::DesignatorMaximum(DesignatorMaximum::new_unchecked(raw))
386                }
387                StringIndex::PREFIX => ReportItem::StringIndex(StringIndex::new_unchecked(raw)),
388                StringMinimum::PREFIX => {
389                    ReportItem::StringMinimum(StringMinimum::new_unchecked(raw))
390                }
391                StringMaximum::PREFIX => {
392                    ReportItem::StringMaximum(StringMaximum::new_unchecked(raw))
393                }
394                Delimiter::PREFIX => ReportItem::Delimiter(Delimiter::new_unchecked(raw)),
395                _ => return Err(HidError::ReservedItem(Reserved::new_unchecked(raw))),
396            })
397        }
398    }
399
400    /// Create a new item from raw byte stream, without checking data length.
401    ///
402    /// Items that cannot be recognized will be treated as [`Reserved`](ReportItem::Reserved).
403    /// If you want to fail on unknown items, use
404    /// [`new_strict_unchecked()`](ReportItem::new_strict_unchecked()) instead.
405    ///
406    /// # Safety
407    ///
408    /// You should ensure that the raw data is a valid HID report item.
409    pub unsafe fn new_unchecked(raw: &[u8]) -> Self {
410        match raw[0] & 0b1111_1100 {
411            Input::PREFIX => ReportItem::Input(Input::new_unchecked(raw)),
412            Output::PREFIX => ReportItem::Output(Output::new_unchecked(raw)),
413            Feature::PREFIX => ReportItem::Feature(Feature::new_unchecked(raw)),
414            Collection::PREFIX => ReportItem::Collection(Collection::new_unchecked(raw)),
415            EndCollection::PREFIX => ReportItem::EndCollection(EndCollection::new_unchecked(raw)),
416            UsagePage::PREFIX => ReportItem::UsagePage(UsagePage::new_unchecked(raw)),
417            LogicalMinimum::PREFIX => {
418                ReportItem::LogicalMinimum(LogicalMinimum::new_unchecked(raw))
419            }
420            LogicalMaximum::PREFIX => {
421                ReportItem::LogicalMaximum(LogicalMaximum::new_unchecked(raw))
422            }
423            PhysicalMinimum::PREFIX => {
424                ReportItem::PhysicalMinimum(PhysicalMinimum::new_unchecked(raw))
425            }
426            PhysicalMaximum::PREFIX => {
427                ReportItem::PhysicalMaximum(PhysicalMaximum::new_unchecked(raw))
428            }
429            UnitExponent::PREFIX => ReportItem::UnitExponent(UnitExponent::new_unchecked(raw)),
430            Unit::PREFIX => ReportItem::Unit(Unit::new_unchecked(raw)),
431            ReportSize::PREFIX => ReportItem::ReportSize(ReportSize::new_unchecked(raw)),
432            ReportId::PREFIX => ReportItem::ReportId(ReportId::new_unchecked(raw)),
433            ReportCount::PREFIX => ReportItem::ReportCount(ReportCount::new_unchecked(raw)),
434            Push::PREFIX => ReportItem::Push(Push::new_unchecked(raw)),
435            Pop::PREFIX => ReportItem::Pop(Pop::new_unchecked(raw)),
436            Usage::PREFIX => ReportItem::Usage(Usage::new_unchecked(raw)),
437            UsageMinimum::PREFIX => ReportItem::UsageMinimum(UsageMinimum::new_unchecked(raw)),
438            UsageMaximum::PREFIX => ReportItem::UsageMaximum(UsageMaximum::new_unchecked(raw)),
439            DesignatorIndex::PREFIX => {
440                ReportItem::DesignatorIndex(DesignatorIndex::new_unchecked(raw))
441            }
442            DesignatorMinimum::PREFIX => {
443                ReportItem::DesignatorMinimum(DesignatorMinimum::new_unchecked(raw))
444            }
445            DesignatorMaximum::PREFIX => {
446                ReportItem::DesignatorMaximum(DesignatorMaximum::new_unchecked(raw))
447            }
448            StringIndex::PREFIX => ReportItem::StringIndex(StringIndex::new_unchecked(raw)),
449            StringMinimum::PREFIX => ReportItem::StringMinimum(StringMinimum::new_unchecked(raw)),
450            StringMaximum::PREFIX => ReportItem::StringMaximum(StringMaximum::new_unchecked(raw)),
451            Delimiter::PREFIX => ReportItem::Delimiter(Delimiter::new_unchecked(raw)),
452            _ => ReportItem::Reserved(Reserved::new_unchecked(raw)),
453        }
454    }
455
456    /// Create a new item from raw byte stream in strict mode, without checking data length.
457    ///
458    /// Items that cannot be recognized will be treated as [`HidError::ReservedItem`].
459    /// Also, this is the only error that may be reported.
460    ///
461    /// # Safety
462    ///
463    /// You should ensure that the raw data is a valid HID report item.
464    pub unsafe fn new_strict_unchecked(raw: &[u8]) -> Result<Self, HidError> {
465        Ok(match raw[0] & 0b1111_1100 {
466            Input::PREFIX => ReportItem::Input(Input::new_unchecked(raw)),
467            Output::PREFIX => ReportItem::Output(Output::new_unchecked(raw)),
468            Feature::PREFIX => ReportItem::Feature(Feature::new_unchecked(raw)),
469            Collection::PREFIX => ReportItem::Collection(Collection::new_unchecked(raw)),
470            EndCollection::PREFIX => ReportItem::EndCollection(EndCollection::new_unchecked(raw)),
471            UsagePage::PREFIX => ReportItem::UsagePage(UsagePage::new_unchecked(raw)),
472            LogicalMinimum::PREFIX => {
473                ReportItem::LogicalMinimum(LogicalMinimum::new_unchecked(raw))
474            }
475            LogicalMaximum::PREFIX => {
476                ReportItem::LogicalMaximum(LogicalMaximum::new_unchecked(raw))
477            }
478            PhysicalMinimum::PREFIX => {
479                ReportItem::PhysicalMinimum(PhysicalMinimum::new_unchecked(raw))
480            }
481            PhysicalMaximum::PREFIX => {
482                ReportItem::PhysicalMaximum(PhysicalMaximum::new_unchecked(raw))
483            }
484            UnitExponent::PREFIX => ReportItem::UnitExponent(UnitExponent::new_unchecked(raw)),
485            Unit::PREFIX => ReportItem::Unit(Unit::new_unchecked(raw)),
486            ReportSize::PREFIX => ReportItem::ReportSize(ReportSize::new_unchecked(raw)),
487            ReportId::PREFIX => ReportItem::ReportId(ReportId::new_unchecked(raw)),
488            ReportCount::PREFIX => ReportItem::ReportCount(ReportCount::new_unchecked(raw)),
489            Push::PREFIX => ReportItem::Push(Push::new_unchecked(raw)),
490            Pop::PREFIX => ReportItem::Pop(Pop::new_unchecked(raw)),
491            Usage::PREFIX => ReportItem::Usage(Usage::new_unchecked(raw)),
492            UsageMinimum::PREFIX => ReportItem::UsageMinimum(UsageMinimum::new_unchecked(raw)),
493            UsageMaximum::PREFIX => ReportItem::UsageMaximum(UsageMaximum::new_unchecked(raw)),
494            DesignatorIndex::PREFIX => {
495                ReportItem::DesignatorIndex(DesignatorIndex::new_unchecked(raw))
496            }
497            DesignatorMinimum::PREFIX => {
498                ReportItem::DesignatorMinimum(DesignatorMinimum::new_unchecked(raw))
499            }
500            DesignatorMaximum::PREFIX => {
501                ReportItem::DesignatorMaximum(DesignatorMaximum::new_unchecked(raw))
502            }
503            StringIndex::PREFIX => ReportItem::StringIndex(StringIndex::new_unchecked(raw)),
504            StringMinimum::PREFIX => ReportItem::StringMinimum(StringMinimum::new_unchecked(raw)),
505            StringMaximum::PREFIX => ReportItem::StringMaximum(StringMaximum::new_unchecked(raw)),
506            Delimiter::PREFIX => ReportItem::Delimiter(Delimiter::new_unchecked(raw)),
507            _ => return Err(HidError::ReservedItem(Reserved::new_unchecked(raw))),
508        })
509    }
510
511    /// Get prefix part of the item. Equivalent to `item.as_ref()[0]`.
512    pub fn prefix(&self) -> u8 {
513        self.as_ref()[0]
514    }
515
516    /// Get data part of the item. Equivalent to `&item.as_ref()[1..]`.
517    pub fn data(&self) -> &[u8] {
518        &self.as_ref()[1..]
519    }
520}
521
522struct Iter<ByteStreamIter: Iterator<Item = u8>> {
523    byte_stream_iter: ByteStreamIter,
524    usage_page: Option<UsagePage>,
525}
526
527struct StrictIter<ByteStreamIter: Iterator<Item = u8>> {
528    byte_stream_iter: ByteStreamIter,
529    usage_page: Option<UsagePage>,
530}
531
532impl<ByteStreamIter: Iterator<Item = u8>> Iterator for Iter<ByteStreamIter> {
533    type Item = ReportItem;
534    fn next(&mut self) -> Option<Self::Item> {
535        let prefix = self.byte_stream_iter.next()?;
536        let size = __data_size(prefix);
537        let mut storage = [0u8; 5];
538        storage[0] = prefix;
539        for i in 0..size {
540            storage[i + 1] = self.byte_stream_iter.next()?;
541        }
542        let mut item = unsafe { ReportItem::new_unchecked(&storage) };
543        if let ReportItem::UsagePage(usage_page) = &item {
544            self.usage_page = Some(usage_page.clone());
545        }
546        if let Some(usage_page) = &self.usage_page {
547            match &mut item {
548                ReportItem::Usage(usage) => usage.set_usage_page(usage_page.clone()),
549                ReportItem::UsageMinimum(usage_minimum) => {
550                    usage_minimum.set_usage_page(usage_page.clone())
551                }
552                ReportItem::UsageMaximum(usage_maximum) => {
553                    usage_maximum.set_usage_page(usage_page.clone())
554                }
555                _ => (),
556            }
557        }
558        Some(item)
559    }
560}
561
562impl<ByteStreamIter: Iterator<Item = u8>> Iterator for StrictIter<ByteStreamIter> {
563    type Item = Result<ReportItem, HidError>;
564    fn next(&mut self) -> Option<Self::Item> {
565        let prefix = self.byte_stream_iter.next()?;
566        let size = __data_size(prefix);
567        let mut storage = [0u8; 5];
568        storage[0] = prefix;
569        for i in 0..size {
570            storage[i + 1] = self.byte_stream_iter.next()?;
571        }
572        let mut item = unsafe { ReportItem::new_strict_unchecked(&storage) };
573        if let Ok(ReportItem::UsagePage(usage_page)) = &item {
574            self.usage_page = Some(usage_page.clone());
575        }
576        if let Some(usage_page) = &self.usage_page {
577            match &mut item {
578                Ok(ReportItem::Usage(usage)) => usage.set_usage_page(usage_page.clone()),
579                Ok(ReportItem::UsageMinimum(usage_minimum)) => {
580                    usage_minimum.set_usage_page(usage_page.clone())
581                }
582                Ok(ReportItem::UsageMaximum(usage_maximum)) => {
583                    usage_maximum.set_usage_page(usage_page.clone())
584                }
585                _ => (),
586            }
587        }
588        Some(item)
589    }
590}
591
592/// Parse a byte stream into a report item iterator.
593///
594/// Items that cannot be recognized will be treated as [`Reserved`](ReportItem::Reserved).
595/// If you want to fail on unknown items, use [`parse_strict()`](parse_strict()) instead.
596///
597/// # Example
598///
599/// ```
600/// use hid_report::parse;
601///
602/// let bytes = [
603///     0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x02, 0x19,
604///     0x00, 0x2A, 0x3C, 0x02, 0x15, 0x00, 0x26, 0x3C, 0x02,
605///     0x95, 0x01, 0x75, 0x10, 0x81, 0x00, 0xC0,
606/// ];
607/// let mut items = parse(bytes);
608/// assert_eq!(items.next().unwrap().to_string(), "Usage Page (Consumer)");
609/// assert_eq!(items.next().unwrap().to_string(), "Usage (Consumer Control)");
610/// assert_eq!(items.next().unwrap().to_string(), "Collection (Application)");
611/// assert_eq!(items.next().unwrap().to_string(), "Report ID (2)");
612/// assert_eq!(items.next().unwrap().to_string(), "Usage Minimum (Undefined)");
613/// assert_eq!(items.next().unwrap().to_string(), "Usage Maximum (AC Format)");
614/// assert_eq!(items.next().unwrap().to_string(), "Logical Minimum (0)");
615/// assert_eq!(items.next().unwrap().to_string(), "Logical Maximum (572)");
616/// assert_eq!(items.next().unwrap().to_string(), "Report Count (1)");
617/// assert_eq!(items.next().unwrap().to_string(), "Report Size (16)");
618/// assert_eq!(
619///     items.next().unwrap().to_string(),
620///     "Input (Data, Array, Absolute, No Wrap, Linear, Preferred State, No Null Position)"
621/// );
622/// assert_eq!(items.next().unwrap().to_string(), "End Collection");
623/// assert_eq!(items.next(), None);
624/// ```
625pub fn parse<ByteStream: IntoIterator<Item = u8>>(
626    byte_stream: ByteStream,
627) -> impl Iterator<Item = ReportItem> {
628    Iter {
629        byte_stream_iter: byte_stream.into_iter(),
630        usage_page: None,
631    }
632}
633
634/// Parse a byte stream into a report item iterator in strict mode.
635///
636/// Items that cannot be recognized will be treated as [`HidError::ReservedItem`].
637/// Also, this is the only error that may be reported.
638pub fn parse_strict<ByteStream: IntoIterator<Item = u8>>(
639    byte_stream: ByteStream,
640) -> impl Iterator<Item = Result<ReportItem, HidError>> {
641    StrictIter {
642        byte_stream_iter: byte_stream.into_iter(),
643        usage_page: None,
644    }
645}
646
647/// Dump items into a byte stream.
648pub fn dump<'a, ItemStream: IntoIterator<Item = &'a ReportItem>>(
649    item_stream: ItemStream,
650) -> Vec<u8> {
651    let mut v = Vec::new();
652    for item in item_stream {
653        v.extend_from_slice(item.as_ref());
654    }
655    v
656}
657
658/// Print items to string in a pretty way.
659///
660/// # Example
661///
662/// ```
663/// use hid_report::{parse, pretty_print};
664///
665/// let bytes = [
666///     0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x02, 0x19,
667///     0x00, 0x2A, 0x3C, 0x02, 0x15, 0x00, 0x26, 0x3C, 0x02,
668///     0x95, 0x01, 0x75, 0x10, 0x81, 0x00, 0xC0,
669/// ];
670/// let items = parse(bytes).collect::<Vec<_>>();
671///
672/// const EXPECTED: &str = indoc::indoc! {"
673///     0x05, 0x0C        // Usage Page (Consumer)
674///     0x09, 0x01        // Usage (Consumer Control)
675///     0xA1, 0x01        //   Collection (Application)
676///     0x85, 0x02        //   Report ID (2)
677///     0x19, 0x00        //   Usage Minimum (Undefined)
678///     0x2A, 0x3C, 0x02  //   Usage Maximum (AC Format)
679///     0x15, 0x00        //   Logical Minimum (0)
680///     0x26, 0x3C, 0x02  //   Logical Maximum (572)
681///     0x95, 0x01        //   Report Count (1)
682///     0x75, 0x10        //   Report Size (16)
683///     0x81, 0x00        //   Input (Data, Array, Absolute, No Wrap, Linear, Preferred State, No Null Position)
684///     0xC0              // End Collection"
685/// };
686///
687/// assert_eq!(pretty_print(&items), EXPECTED);
688/// ```
689pub fn pretty_print<'a, ItemStream: IntoIterator<Item = &'a ReportItem>>(
690    item_stream: ItemStream,
691) -> String {
692    let mut max_len = 0;
693    let mut tmp = Vec::new();
694    let mut tab: usize = 0;
695    for item in item_stream {
696        match item {
697            ReportItem::Collection(_) | ReportItem::Push(_) => tab += 1,
698            ReportItem::EndCollection(_) | ReportItem::Pop(_) => tab = tab.saturating_sub(1),
699            _ => (),
700        }
701        max_len = std::cmp::max(max_len, item.as_ref().len());
702        tmp.push((
703            item.as_ref()
704                .iter()
705                .map(|byte| format!("{:#04X}", byte))
706                .collect::<Vec<_>>()
707                .join(", "),
708            item.to_string(),
709            tab * 2 + 1,
710        ));
711    }
712    let width_of_raw = max_len * 6;
713    tmp.into_iter()
714        .map(|(raw, comment, tab)| format!("{:<width_of_raw$}//{:<tab$}{}", raw, ' ', comment))
715        .collect::<Vec<_>>()
716        .join("\n")
717}