Skip to main content

hid_types/item/
mod.rs

1//! Structured HID Report Descriptor Items.
2//!
3//! This module contains fully populated descriptor items (containing values).
4//! See the [`id`][crate::id] module for just the item ID values.
5
6use crate::id::CollectionType;
7use crate::id::usage::UsagePage;
8use crate::id::{InputFlags, OutputFeatureFlags};
9use crate::id::{Unit, UnitExponent};
10use crate::item::usage::{ExtendedUsage, Usage};
11
12pub mod usage;
13
14/// An HID report descriptor Item.
15#[derive(Clone, PartialEq)]
16#[cfg_attr(feature = "std", derive(Debug))]
17pub enum Item {
18    /// An undecoded "long" item.
19    ///
20    /// According to the spec
21    /// ([Device Class Definition for Human Interface Devices](https://www.usb.org/document-library/device-class-definition-hid-111),
22    /// section 6.2.2.3), there are no standard-defined uses for this encoding.
23    Long(LongItem),
24    /// A __Main__ item.
25    ///
26    /// See [Device Class Definition for Human Interface Devices](https://www.usb.org/document-library/device-class-definition-hid-111),
27    /// section 6.2.2.4.
28    Main(Main),
29    /// A __Global__ item.
30    Global(Global),
31    /// A __Local__ item.
32    Local(Local),
33    /// A reserved item value.
34    ///
35    /// This should not be used; the descriptor cannot be properly decoded.
36    Reserved(u8, Option<i32>),
37}
38
39impl Item {
40    #[cfg(feature = "std")]
41    /// A string identifying the type of the item.
42    pub fn type_note(&self) -> &'static str {
43        match self {
44            Item::Long(_) => "[long]",
45            Item::Main(_) => "[main]",
46            Item::Global(_) => "[global]",
47            Item::Local(_) => "[local]",
48            Item::Reserved(_, _) => "[reserved]",
49        }
50    }
51}
52
53/// A __Main__ item.
54///
55/// See [Device Class Definition for Human Interface Devices](https://www.usb.org/document-library/device-class-definition-hid-111),
56/// section 6.2.2.4.
57#[derive(Clone, PartialEq)]
58#[cfg_attr(feature = "std", derive(Debug))]
59pub enum Main {
60    /// An __Input__ item.
61    Input(InputFlags),
62    /// An __Output__ item.
63    Output(OutputFeatureFlags),
64    /// A __Feature__ item.
65    Feature(OutputFeatureFlags),
66    /// A __Collection__ item.
67    Collection(CollectionType),
68    /// An __End Collection__ item.
69    EndCollection,
70    /// A reserved main item type.
71    Reserved(u8, Option<i32>),
72}
73
74/// A __Global__ item.
75#[derive(Clone, PartialEq)]
76#[cfg_attr(feature = "std", derive(Debug))]
77pub enum Global {
78    /// A __Usage Page__ item.
79    ///
80    /// The Usage Page is a 16-bit value that identifies a specific table of Usage ID values.
81    /// Future __Usage__ items will select a Usage ID from that Usage Page table.
82    UsagePage(UsagePage),
83    /// A __Logical Minimum__ item.
84    LogicalMinimum(i32),
85    /// A __Logical Maximum__ item.
86    LogicalMaximum(i32),
87    /// A __Physical Minimum__ item.
88    PhysicalMinimum(i32),
89    /// A __Physical Maximum__ item.
90    PhysicalMaximum(i32),
91    /// A __Unit Exponent__ item.
92    ///
93    /// The HID specification is unclear on the range, but the common practice
94    /// is to treat this value as a 4-bit signed integer (-8 to +7).
95    UnitExponent(UnitExponent),
96    /// A __Unit__ item.
97    Unit(Unit),
98    /// A __Report Size__ item.
99    ReportSize(u32),
100    /// A __Report ID__ item.
101    ReportId(u8),
102    /// A __Report Count__ item.
103    ReportCount(u32),
104    /// A __Push__ item.
105    Push,
106    /// A __Pop__ item.
107    Pop,
108    /// A reserved item type.
109    Reserved(u8, Option<i32>),
110}
111
112/// A __Local__ item.
113#[derive(Clone, PartialEq)]
114#[cfg_attr(feature = "std", derive(Debug))]
115pub enum Local {
116    /// A __Usage__ item.
117    Usage(Usage),
118    /// A __Usage__ item, that also contains a __Usage Page__.
119    ExtendedUsage(ExtendedUsage),
120    /// A __Usage Minimum__ item.
121    UsageMinimum(u32),
122    /// A __Usage Maximum__ item.
123    UsageMaximum(u32),
124    /// A __Designator Index__ item.
125    DesignatorIndex(u32),
126    /// A __Designator Minimum__ item.
127    DesignatorMinimum(u32),
128    /// A __Designator Maximum__ item.
129    DesignatorMaximum(u32),
130    /// A __String Index__ item.
131    StringIndex(u32),
132    /// A __String Minimum__ item.
133    StringMinimum(u32),
134    /// A __String Maximum__ item.
135    StringMaximum(u32),
136    /// A __Delimiter__ item.
137    Delimiter(bool),
138    /// A reserved type.
139    Reserved(u8, Option<i32>),
140}
141
142/// An item using the "long" encoding.
143///
144/// According to the spec
145/// ([Device Class Definition for Human Interface Devices](https://www.usb.org/document-library/device-class-definition-hid-111),
146/// section 6.2.2.3), there are no standard-defined uses for this encoding.
147#[derive(Clone, PartialEq)]
148#[cfg_attr(feature = "std", derive(Debug))]
149#[expect(missing_docs)]
150pub struct LongItem {
151    pub tag: u8,
152    #[cfg(feature = "std")]
153    pub data: std::vec::Vec<u8>,
154}
155
156#[cfg(feature = "std")]
157mod std_impls {
158    use std::fmt::{self, Display};
159
160    use super::*;
161
162    impl Display for Item {
163        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164            match self {
165                Item::Long(item) => {
166                    write!(f, "tag {:#04x} [{} bytes]", item.tag, item.data.len())
167                }
168                Item::Main(item) => {
169                    write!(f, "{item}")
170                }
171                Item::Global(item) => {
172                    write!(f, "{item}")
173                }
174                Item::Local(item) => write!(f, "{item}"),
175                Item::Reserved(ty, _) => write!(f, "reserved type {ty}"),
176            }
177        }
178    }
179
180    impl Display for Main {
181        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182            match self {
183                Main::Input(flags) => {
184                    write!(f, "Input: {flags:?}")
185                }
186                Main::Output(flags) => {
187                    write!(f, "Output: {flags:?}")
188                }
189                Main::Feature(flags) => {
190                    write!(f, "Feature: {flags:?}")
191                }
192                Main::Collection(coll) => {
193                    write!(f, "Collection: {coll:?}")
194                }
195                Main::EndCollection => {
196                    write!(f, "EndCollection")
197                }
198                Main::Reserved(tag, data) => {
199                    write!(f, "Reserved tag {tag:#04X}")?;
200                    if let Some(data) = &data {
201                        write!(f, "with data {data:#04x}")?;
202                    }
203                    Ok(())
204                }
205            }
206        }
207    }
208
209    impl Display for Global {
210        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211            match self {
212                Global::UsagePage(page) => {
213                    write!(f, "Usage Page: {page:?}")
214                }
215                Global::LogicalMinimum(value) => write!(f, "Logical Minimum: {value}"),
216                Global::LogicalMaximum(value) => write!(f, "Logical Maximum: {value}"),
217                Global::PhysicalMinimum(value) => write!(f, "Physical Minimum: {value}"),
218                Global::PhysicalMaximum(value) => write!(f, "Physical Maximum: {value}"),
219                Global::UnitExponent(value) => write!(f, "Unit Exponent: {value}"),
220                Global::Unit(unit) => write!(f, "Unit: {unit:?}"),
221                Global::Push => write!(f, "Push"),
222                Global::Pop => write!(f, "Pop"),
223                Global::Reserved(tag, data) => {
224                    write!(f, "Reserved tag {tag:#04X}")?;
225                    if let Some(data) = &data {
226                        write!(f, "with data {data:#04x}")?;
227                    }
228                    Ok(())
229                }
230                Global::ReportSize(value) => write!(f, "Report Size: {value}"),
231                Global::ReportId(value) => write!(f, "Report ID: {value}"),
232                Global::ReportCount(value) => write!(f, "Report Count: {value}"),
233            }
234        }
235    }
236
237    impl Display for Local {
238        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239            match self {
240                Local::Usage(usage) => {
241                    write!(f, "Usage: {usage}")
242                }
243                Local::ExtendedUsage(extended_usage) => {
244                    write!(f, "Extended Usage: {extended_usage:?}")
245                }
246                Local::UsageMinimum(value) => {
247                    write!(f, "Usage Minimum: {value}")
248                }
249                Local::UsageMaximum(value) => {
250                    write!(f, "Usage Maximum: {value}")
251                }
252                Local::DesignatorIndex(value) => write!(f, "Designator Index: {value}"),
253                Local::DesignatorMinimum(value) => write!(f, "Designator Minimum: {value}"),
254                Local::DesignatorMaximum(value) => write!(f, "Designator Maximum: {value}"),
255                Local::StringIndex(value) => write!(f, "String Index: {value}"),
256                Local::StringMinimum(value) => write!(f, "String Minimum: {value}"),
257                Local::StringMaximum(value) => write!(f, "String Maximum: {value}"),
258                Local::Delimiter(value) => write!(f, "Delimiter: {value}"),
259                Local::Reserved(tag, data) => {
260                    write!(f, "Reserved tag {tag:#04X}")?;
261                    if let Some(data) = &data {
262                        write!(f, "with data {data:#04x}")?;
263                    }
264                    Ok(())
265                }
266            }
267        }
268    }
269}