usb_ids/
lib.rs

1//!
2//! Rust wrappers for the [USB ID Repository](http://www.linux-usb.org/usb-ids.html).
3//!
4//! The USB ID Repository is the canonical source of USB device information for most
5//! Linux userspaces; this crate vendors the USB ID database to allow non-Linux hosts to
6//! access the same canonical information.
7//!
8//! # Usage
9//!
10//! Iterating over all known vendors:
11//!
12//! ```rust
13//! use usb_ids::Vendors;
14//!
15//! for vendor in Vendors::iter() {
16//!     for device in vendor.devices() {
17//!         println!("vendor: {}, device: {}", vendor.name(), device.name());
18//!     }
19//! }
20//! ```
21//!
22//! Iterating over all known classes:
23//!
24//! ```rust
25//! use usb_ids::Classes;
26//!
27//! for class in Classes::iter() {
28//!     println!("class: {}", class.name());
29//!     for subclass in class.sub_classes() {
30//!         println!("\tsubclass: {}", subclass.name());
31//!         for protocol in subclass.protocols() {
32//!            println!("\t\tprotocol: {}", protocol.name());
33//!         }
34//!     }
35//! }
36//! ```
37//!
38//! See the individual documentation for each structure for more details.
39//!
40
41#![warn(missing_docs)]
42
43include!(concat!(env!("OUT_DIR"), "/usb_ids.cg.rs"));
44
45/// Represents a generic USB ID in the USB database.
46///
47/// Not designed to be used directly; use one of the type aliases instead.
48#[derive(Clone, Copy, Debug, PartialEq, Eq)]
49pub struct UsbId<const ID: u8, T> {
50    id: T,
51    name: &'static str,
52}
53
54impl<const ID: u8, T: Copy> UsbId<ID, T> {
55    /// Returns the type's ID.
56    pub fn id(&self) -> T {
57        self.id
58    }
59
60    /// Returns the type's name.
61    pub fn name(&self) -> &'static str {
62        self.name
63    }
64}
65
66/// Represents a generic USB ID in the USB database with children IDs.
67///
68/// Not designed to be used directly; use one of the type aliases instead.
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
70pub struct UsbIdWithChildren<T: Copy, C: 'static> {
71    id: T,
72    name: &'static str,
73    children: &'static [C],
74}
75
76impl<T: Copy, C: 'static> UsbIdWithChildren<T, C> {
77    /// Returns the type's ID.
78    pub fn id(&self) -> T {
79        self.id
80    }
81
82    /// Returns the type's name.
83    pub fn name(&self) -> &'static str {
84        self.name
85    }
86
87    /// Returns an iterator over the type's children.
88    fn children(&self) -> impl Iterator<Item = &'static C> {
89        self.children.iter()
90    }
91}
92
93/// An abstraction for iterating over all vendors in the USB database.
94pub struct Vendors;
95impl Vendors {
96    /// Returns an iterator over all vendors in the USB database.
97    pub fn iter() -> impl Iterator<Item = &'static Vendor> {
98        USB_IDS.values()
99    }
100}
101
102/// An abstraction for iterating over all classes in the USB database.
103pub struct Classes;
104impl Classes {
105    /// Returns an iterator over all classes in the USB database.
106    pub fn iter() -> impl Iterator<Item = &'static Class> {
107        USB_CLASSES.values()
108    }
109}
110
111/// An abstraction for iterating over all languages in the USB database.
112///
113/// ```
114/// use usb_ids::Languages;
115/// for language in Languages::iter() {
116///    println!("language: {}", language.name());
117///    for dialect in language.dialects() {
118///         println!("\tdialect: {}", dialect.name());
119///    }
120/// }
121/// ```
122pub struct Languages;
123impl Languages {
124    /// Returns an iterator over all languages in the USB database.
125    pub fn iter() -> impl Iterator<Item = &'static Language> {
126        USB_LANGS.values()
127    }
128}
129
130/// An abstraction for iterating over all HID usage pages in the USB database.
131///
132/// ```
133/// use usb_ids::HidUsagePages;
134///
135/// for page in HidUsagePages::iter() {
136///     println!("page: {}", page.name());
137///     for usage in page.usages() {
138///         println!("\tusage: {}", usage.name());
139///     }
140/// }
141/// ```
142pub struct HidUsagePages;
143impl HidUsagePages {
144    /// Returns an iterator over all HID usage pages in the USB database.
145    pub fn iter() -> impl Iterator<Item = &'static HidUsagePage> {
146        USB_HUTS.values()
147    }
148}
149
150/// Represents a USB device vendor in the USB database.
151///
152/// Every device vendor has a vendor ID, a pretty name, and a
153/// list of associated [`Device`]s.
154#[derive(Clone, Copy, Debug, PartialEq, Eq)]
155pub struct Vendor {
156    id: u16,
157    name: &'static str,
158    devices: &'static [Device],
159}
160
161impl Vendor {
162    /// Returns the vendor's ID.
163    pub fn id(&self) -> u16 {
164        self.id
165    }
166
167    /// Returns the vendor's name.
168    pub fn name(&self) -> &'static str {
169        self.name
170    }
171
172    /// Returns an iterator over the vendor's [`Device`]s.
173    pub fn devices(&self) -> impl Iterator<Item = &'static Device> {
174        self.devices.iter()
175    }
176}
177
178/// Represents a single device in the USB database.
179///
180/// Every device has a corresponding vendor, a device ID, a pretty name,
181/// and a list of associated [`Interface`]s.
182#[derive(Clone, Copy, Debug, PartialEq, Eq)]
183pub struct Device {
184    vendor_id: u16,
185    id: u16,
186    name: &'static str,
187    interfaces: &'static [Interface],
188}
189
190impl Device {
191    /// Returns the [`Device`] corresponding to the given vendor and product IDs,
192    /// or `None` if no such device exists in the DB.
193    ///
194    /// ```
195    /// use usb_ids::Device;
196    /// let device = Device::from_vid_pid(0x1d6b, 0x0003).unwrap();
197    /// assert_eq!(device.name(), "3.0 root hub");
198    /// ```
199    pub fn from_vid_pid(vid: u16, pid: u16) -> Option<&'static Device> {
200        let vendor = Vendor::from_id(vid);
201
202        vendor.and_then(|v| v.devices().find(|d| d.id == pid))
203    }
204
205    /// Returns the [`Vendor`] that this device belongs to.
206    ///
207    /// Looking up a vendor by device is cheap (`O(1)`).
208    pub fn vendor(&self) -> &'static Vendor {
209        USB_IDS.get(&self.vendor_id).unwrap()
210    }
211
212    /// Returns a tuple of (vendor id, device/"product" id) for this device.
213    ///
214    /// This is convenient for interactions with other USB libraries.
215    pub fn as_vid_pid(&self) -> (u16, u16) {
216        (self.vendor_id, self.id)
217    }
218
219    /// Returns the device's ID.
220    pub fn id(&self) -> u16 {
221        self.id
222    }
223
224    /// Returns the device's name.
225    pub fn name(&self) -> &'static str {
226        self.name
227    }
228
229    /// Returns an iterator over the device's [`Interface`]s.
230    ///
231    /// **NOTE**: The USB database does not include interface information for
232    /// most devices. This list is not authoritative.
233    pub fn interfaces(&self) -> impl Iterator<Item = &'static Interface> {
234        self.interfaces.iter()
235    }
236}
237
238/// Represents an interface to a USB device in the USB database.
239///
240/// Every interface has an interface ID (which is an index on the device)
241/// and a pretty name.
242///
243/// **NOTE**: The USB database is not a canonical or authoritative source
244/// of interface information for devices. Users who wish to discover interfaces
245/// on their USB devices should query those devices directly.
246#[derive(Clone, Copy, Debug, PartialEq, Eq)]
247pub struct Interface {
248    id: u8,
249    name: &'static str,
250}
251
252impl Interface {
253    /// Returns the interface's ID.
254    pub fn id(&self) -> u8 {
255        self.id
256    }
257
258    /// Returns the interface's name.
259    pub fn name(&self) -> &'static str {
260        self.name
261    }
262}
263
264/// Represents a USB device class in the USB database.
265///
266/// Every device class has a class ID, a pretty name, and a
267/// list of associated [`SubClass`]s.
268///
269/// ```
270/// use usb_ids::{Class, Classes, FromId};
271/// let class = Class::from_id(0x03).unwrap();
272/// assert_eq!(class.name(), "Human Interface Device");
273/// ```
274#[derive(Clone, Copy, Debug, PartialEq, Eq)]
275pub struct Class {
276    id: u8,
277    name: &'static str,
278    sub_classes: &'static [SubClass],
279}
280
281impl Class {
282    /// Returns the class's ID.
283    pub fn id(&self) -> u8 {
284        self.id
285    }
286
287    /// Returns the class's name.
288    pub fn name(&self) -> &'static str {
289        self.name
290    }
291
292    /// Returns an iterator over the class's [`SubClass`]s.
293    pub fn sub_classes(&self) -> impl Iterator<Item = &'static SubClass> {
294        self.sub_classes.iter()
295    }
296}
297
298/// Represents a class subclass in the USB database. Subclasses are part of the
299/// USB class code triplet (base class, subclass, protocol).
300///
301/// Contained within a [`Class`] and may contain a list of associated
302/// [`Protocol`]s.
303#[derive(Clone, Copy, Debug, PartialEq, Eq)]
304pub struct SubClass {
305    class_id: u8,
306    id: u8,
307    name: &'static str,
308    protocols: &'static [Protocol],
309}
310
311impl SubClass {
312    /// Returns the [`SubClass`] corresponding to the given class and subclass IDs,
313    /// or `None` if no such subclass exists in the DB.
314    ///
315    /// ```
316    /// use usb_ids::SubClass;
317    /// let subclass = SubClass::from_cid_scid(0x02, 0x03).unwrap();
318    /// assert_eq!(subclass.name(), "Telephone");
319    ///
320    /// assert!(SubClass::from_cid_scid(0x3c, 0x02).is_none());
321    /// ```
322    pub fn from_cid_scid(class_id: u8, id: u8) -> Option<&'static Self> {
323        let class = Class::from_id(class_id);
324
325        class.and_then(|c| c.sub_classes().find(|s| s.id == id))
326    }
327
328    /// Returns the [`Class`] that this subclass belongs to.
329    ///
330    /// Looking up a class by subclass is cheap (`O(1)`).
331    ///
332    /// ```
333    /// use usb_ids::SubClass;
334    /// let subclass = SubClass::from_cid_scid(0x02, 0x03).unwrap();
335    /// let class = subclass.class();
336    /// assert_eq!(class.id(), 0x02);
337    /// ```
338    pub fn class(&self) -> &'static Class {
339        USB_CLASSES.get(&self.class_id).unwrap()
340    }
341
342    /// Returns a tuple of (class id, subclass id) for this subclass.
343    ///
344    /// This is convenient for interactions with other USB libraries.
345    pub fn as_cid_scid(&self) -> (u8, u8) {
346        (self.class_id, self.id)
347    }
348
349    /// Returns the subclass' ID.
350    pub fn id(&self) -> u8 {
351        self.id
352    }
353
354    /// Returns the subclass' name.
355    pub fn name(&self) -> &'static str {
356        self.name
357    }
358
359    /// Returns an iterator over the subclasses's [`Protocol`]s.
360    ///
361    /// **NOTE**: The USB database nor USB-IF includes protocol information for
362    /// all subclassess. This list is not authoritative.
363    pub fn protocols(&self) -> impl Iterator<Item = &'static Protocol> {
364        self.protocols.iter()
365    }
366}
367
368/// These are tags for UsbId type aliases to make them unique and allow a
369/// [`FromId`] for each alias. The values are arbitrary but must be unique.
370///
371/// [`std::marker::PhantomData`] would be nicer but was unable to figure out a
372/// generic way to add the _tag: PhantomData in the ToToken trait
373/// implementation within build.rs
374const PROTOCOL_TAG: u8 = 0;
375const AT_TAG: u8 = 1;
376const HID_TAG: u8 = 2;
377const HID_TYPE_TAG: u8 = 3;
378const HID_USAGE_TAG: u8 = 4;
379const BIAS_TAG: u8 = 5;
380const PHY_TAG: u8 = 6;
381const DIALECT_TAG: u8 = 7;
382const HCC_TAG: u8 = 8;
383const VT_TAG: u8 = 9;
384
385/// Represents a subclass protocol in the USB database.
386///
387/// Protocols are part of the USB class code triplet (base class, subclass,
388/// protocol), contained within a [`SubClass`].
389pub type Protocol = UsbId<PROTOCOL_TAG, u8>;
390
391impl Protocol {
392    /// Returns the [`Protocol`] corresponding to the given class, subclass, and protocol IDs,
393    /// or `None` if no such protocol exists in the DB.
394    ///
395    /// ```
396    /// use usb_ids::Protocol;
397    /// let protocol = Protocol::from_cid_scid_pid(0x02, 0x02, 0x05).unwrap();
398    /// assert_eq!(protocol.name(), "AT-commands (3G)");
399    /// ```
400    pub fn from_cid_scid_pid(class_id: u8, subclass_id: u8, id: u8) -> Option<&'static Self> {
401        let subclass = SubClass::from_cid_scid(class_id, subclass_id);
402
403        subclass.and_then(|s| s.protocols().find(|p| p.id == id))
404    }
405}
406
407/// Represents an audio terminal type in the USB database.
408///
409/// ```
410/// use usb_ids::{AudioTerminal, FromId};
411/// let audio_terminal = AudioTerminal::from_id(0x0201).unwrap();
412/// assert_eq!(audio_terminal.name(), "Microphone");
413/// ```
414pub type AudioTerminal = UsbId<AT_TAG, u16>;
415
416/// Represents a HID descriptor type in the USB database.
417///
418/// ```
419/// use usb_ids::{Hid, FromId};
420/// let hid = Hid::from_id(0x22).unwrap();
421/// assert_eq!(hid.name(), "Report");
422/// ```
423pub type Hid = UsbId<HID_TAG, u8>;
424
425/// Represents a HID descriptor item type in the USB database.
426///
427/// ```
428/// use usb_ids::{HidItemType, FromId};
429/// let hid_item_type = HidItemType::from_id(0xb4).unwrap();
430/// assert_eq!(hid_item_type.name(), "Pop");
431/// ```
432pub type HidItemType = UsbId<HID_TYPE_TAG, u8>;
433
434/// Represents a HID usage page in the USB database.
435///
436/// Every HID usage page has a usage page ID, a pretty name, and a list of
437/// associated [`HidUsage`]s.
438///
439/// ```
440/// use usb_ids::{HidUsagePage, FromId};
441/// let hid_usage_page = HidUsagePage::from_id(0x01).unwrap();
442/// assert_eq!(hid_usage_page.name(), "Generic Desktop Controls");
443///
444/// for usage in hid_usage_page.usages() {
445///   println!("usage: {}", usage.name());
446/// }
447/// ```
448pub type HidUsagePage = UsbIdWithChildren<u8, HidUsage>;
449
450impl HidUsagePage {
451    /// Returns an iterator over the page's [`HidUsage`]s.
452    pub fn usages(&self) -> impl Iterator<Item = &'static HidUsage> {
453        self.children()
454    }
455}
456
457/// Represents a HID usage type in the USB database.
458///
459/// ```
460/// use usb_ids::{HidUsage, HidUsagePage, FromId};
461///
462/// let hid_usage_page = HidUsagePage::from_id(0x01).unwrap();
463/// for usage in hid_usage_page.usages() {
464///    println!("usage: {}", usage.name());
465/// }
466/// ```
467pub type HidUsage = UsbId<HID_USAGE_TAG, u16>;
468
469impl HidUsage {
470    /// Returns the [`HidUsage`] corresponding to the given usage page and usage ID,
471    /// or `None` if no such usage exists in the DB.
472    ///
473    /// ```
474    /// use usb_ids::HidUsage;
475    /// let hid_usage = HidUsage::from_pageid_uid(0x01, 0x002).unwrap();
476    /// assert_eq!(hid_usage.name(), "Mouse");
477    /// ```
478    pub fn from_pageid_uid(page_id: u8, id: u16) -> Option<&'static Self> {
479        let page = HidUsagePage::from_id(page_id)?;
480
481        page.children().find(|u| u.id() == id)
482    }
483}
484
485/// Represents physical descriptor bias type in the USB database.
486///
487/// ```
488/// use usb_ids::{Bias, FromId};
489/// let bias = Bias::from_id(0x02).unwrap();
490/// assert_eq!(bias.name(), "Left Hand");
491/// ```
492pub type Bias = UsbId<BIAS_TAG, u8>;
493
494/// Represents physical descriptor item type in the USB database.
495///
496/// ```
497/// use usb_ids::{Phy, FromId};
498/// let phy = Phy::from_id(0x25).unwrap();
499/// assert_eq!(phy.name(), "Fifth Toe");
500/// ```
501pub type Phy = UsbId<PHY_TAG, u8>;
502
503/// Represents a language type in the USB database.
504///
505/// Languages have a language ID, a pretty name, and a list of associated
506/// [`Dialect`]s.
507///
508/// ```
509/// use usb_ids::{Language, FromId};
510/// let language = Language::from_id(0x000c).unwrap();
511/// assert_eq!(language.name(), "French");
512///
513/// for dialect in language.dialects() {
514///   println!("dialect: {}", dialect.name());
515/// }
516/// ```
517pub type Language = UsbIdWithChildren<u16, Dialect>;
518
519impl Language {
520    /// Returns an iterator over the language's [`Dialect`]s.
521    pub fn dialects(&self) -> impl Iterator<Item = &'static Dialect> {
522        self.children()
523    }
524}
525
526/// Represents a language dialect in the USB database.
527///
528/// ```
529/// use usb_ids::{Dialect, Language, FromId};
530/// let lang = Language::from_id(0x0007).unwrap();
531///
532/// println!("language: {}", lang.name());
533/// for dialect in lang.dialects() {
534///    println!("\tdialect: {}", dialect.name());
535/// }
536/// ```
537pub type Dialect = UsbId<DIALECT_TAG, u8>;
538
539impl Dialect {
540    /// Returns the [`Dialect`] corresponding to the given language and dialect IDs,
541    /// or `None` if no such dialect exists in the DB.
542    ///
543    /// ```
544    /// use usb_ids::Dialect;
545    /// let dialect = Dialect::from_lid_did(0x0007, 0x02).unwrap();
546    /// assert_eq!(dialect.name(), "Swiss");
547    /// ```
548    pub fn from_lid_did(language_id: u16, id: u8) -> Option<&'static Self> {
549        let language = Language::from_id(language_id)?;
550
551        language.children().find(|d| d.id() == id)
552    }
553}
554
555/// Represents a HID descriptor country code in the USB database.
556///
557/// ```
558/// use usb_ids::{HidCountryCode, FromId};
559/// let hid_country_code = HidCountryCode::from_id(0x29).unwrap();
560/// assert_eq!(hid_country_code.name(), "Switzerland");
561/// ```
562pub type HidCountryCode = UsbId<HCC_TAG, u8>;
563
564/// Represents a video class terminal type in the USB database.
565///
566/// ```
567/// use usb_ids::{VideoTerminal, FromId};
568/// let video_terminal = VideoTerminal::from_id(0x0101).unwrap();
569/// assert_eq!(video_terminal.name(), "USB Streaming");
570/// ```
571pub type VideoTerminal = UsbId<VT_TAG, u16>;
572
573/// A convenience trait for retrieving a top-level entity (like a [`Vendor`]) from the USB
574/// database by its unique ID.
575///
576/// ```
577/// use usb_ids::{FromId, Vendor};
578/// let vendor = Vendor::from_id(0x1d6b).unwrap();
579/// assert_eq!(vendor.name(), "Linux Foundation");
580/// ```
581pub trait FromId<T> {
582    /// Returns the entity corresponding to `id`, or `None` if none exists.
583    fn from_id(id: T) -> Option<&'static Self>;
584}
585
586impl FromId<u16> for Vendor {
587    fn from_id(id: u16) -> Option<&'static Self> {
588        USB_IDS.get(&id)
589    }
590}
591
592impl FromId<u8> for Class {
593    fn from_id(id: u8) -> Option<&'static Self> {
594        USB_CLASSES.get(&id)
595    }
596}
597
598impl FromId<u16> for AudioTerminal {
599    fn from_id(id: u16) -> Option<&'static Self> {
600        USB_AUDIO_TERMINALS.get(&id)
601    }
602}
603
604impl FromId<u8> for Hid {
605    fn from_id(id: u8) -> Option<&'static Self> {
606        USB_HID_IDS.get(&id)
607    }
608}
609
610impl FromId<u8> for HidItemType {
611    fn from_id(id: u8) -> Option<&'static Self> {
612        USB_HID_R_TYPES.get(&id)
613    }
614}
615
616impl FromId<u8> for HidUsagePage {
617    fn from_id(id: u8) -> Option<&'static Self> {
618        USB_HUTS.get(&id)
619    }
620}
621
622impl FromId<u8> for Bias {
623    fn from_id(id: u8) -> Option<&'static Self> {
624        USB_BIASES.get(&id)
625    }
626}
627
628impl FromId<u8> for Phy {
629    fn from_id(id: u8) -> Option<&'static Self> {
630        USB_PHYS.get(&id)
631    }
632}
633
634impl FromId<u16> for Language {
635    fn from_id(id: u16) -> Option<&'static Self> {
636        USB_LANGS.get(&id)
637    }
638}
639
640impl FromId<u8> for HidCountryCode {
641    fn from_id(id: u8) -> Option<&'static Self> {
642        USB_HID_CCS.get(&id)
643    }
644}
645
646impl FromId<u16> for VideoTerminal {
647    fn from_id(id: u16) -> Option<&'static Self> {
648        USB_VIDEO_TERMINALS.get(&id)
649    }
650}
651
652#[cfg(test)]
653mod tests {
654    use super::*;
655
656    #[test]
657    fn test_from_id() {
658        let vendor = Vendor::from_id(0x1d6b).unwrap();
659
660        assert_eq!(vendor.name(), "Linux Foundation");
661        assert_eq!(vendor.id(), 0x1d6b);
662    }
663
664    #[test]
665    fn test_vendor_devices() {
666        let vendor = Vendor::from_id(0x1d6b).unwrap();
667
668        for device in vendor.devices() {
669            assert_eq!(device.vendor(), vendor);
670            assert!(!device.name().is_empty());
671        }
672    }
673
674    #[test]
675    fn test_from_vid_pid() {
676        let device = Device::from_vid_pid(0x1d6b, 0x0003).unwrap();
677
678        assert_eq!(device.name(), "3.0 root hub");
679
680        let (vid, pid) = device.as_vid_pid();
681
682        assert_eq!(vid, device.vendor().id());
683        assert_eq!(pid, device.id());
684
685        let device2 = Device::from_vid_pid(vid, pid).unwrap();
686
687        assert_eq!(device, device2);
688
689        let last_device = Device::from_vid_pid(0xffee, 0x0100).unwrap();
690        assert_eq!(
691            last_device.name(),
692            "Card Reader Controller RTS5101/RTS5111/RTS5116"
693        );
694    }
695
696    #[test]
697    fn test_class_from_id() {
698        let class = Class::from_id(0x03).unwrap();
699
700        assert_eq!(class.name(), "Human Interface Device");
701        assert_eq!(class.id(), 0x03);
702    }
703
704    #[test]
705    fn test_subclass_from_cid_scid() {
706        let subclass = SubClass::from_cid_scid(0x03, 0x01).unwrap();
707
708        assert_eq!(subclass.name(), "Boot Interface Subclass");
709        assert_eq!(subclass.id(), 0x01);
710    }
711
712    #[test]
713    fn test_protocol_from_cid_scid_pid() {
714        let protocol = Protocol::from_cid_scid_pid(0x03, 0x01, 0x01).unwrap();
715
716        assert_eq!(protocol.name(), "Keyboard");
717        assert_eq!(protocol.id(), 0x01);
718
719        let protocol = Protocol::from_cid_scid_pid(0x07, 0x01, 0x03).unwrap();
720
721        assert_eq!(protocol.name(), "IEEE 1284.4 compatible bidirectional");
722        assert_eq!(protocol.id(), 0x03);
723
724        let protocol = Protocol::from_cid_scid_pid(0xff, 0xff, 0xff).unwrap();
725
726        // check last entry for parsing
727        assert_eq!(protocol.name(), "Vendor Specific Protocol");
728        assert_eq!(protocol.id(), 0xff);
729    }
730
731    #[test]
732    fn test_at_from_id() {
733        let at = AudioTerminal::from_id(0x0713).unwrap();
734
735        assert_eq!(at.name(), "Synthesizer");
736        assert_eq!(at.id(), 0x0713);
737    }
738
739    #[test]
740    fn test_hid_from_id() {
741        let hid = Hid::from_id(0x23).unwrap();
742
743        assert_eq!(hid.name(), "Physical");
744        assert_eq!(hid.id(), 0x23);
745    }
746
747    #[test]
748    fn test_hid_type_from_id() {
749        let hid_type = HidItemType::from_id(0xc0).unwrap();
750
751        assert_eq!(hid_type.name(), "End Collection");
752        assert_eq!(hid_type.id(), 0xc0);
753    }
754
755    #[test]
756    fn test_bias_from_id() {
757        let bias = Bias::from_id(0x04).unwrap();
758
759        assert_eq!(bias.name(), "Either Hand");
760        assert_eq!(bias.id(), 0x04);
761    }
762
763    #[test]
764    fn test_phy_from_id() {
765        let phy = Phy::from_id(0x27).unwrap();
766
767        assert_eq!(phy.name(), "Cheek");
768        assert_eq!(phy.id(), 0x27);
769    }
770
771    #[test]
772    fn test_hid_usages_from_id() {
773        let hid_usage_page = HidUsagePage::from_id(0x0d).unwrap();
774
775        assert_eq!(hid_usage_page.name(), "Digitizer");
776        assert_eq!(hid_usage_page.id(), 0x0d);
777
778        let hid_usage = HidUsage::from_pageid_uid(0x0d, 0x01).unwrap();
779
780        assert_eq!(hid_usage.name(), "Digitizer");
781        assert_eq!(hid_usage.id(), 0x01);
782    }
783
784    #[test]
785    fn test_language_from_id() {
786        let language = Language::from_id(0x0007).unwrap();
787
788        assert_eq!(language.name(), "German");
789        assert_eq!(language.id(), 0x0007);
790
791        let dialect = language.dialects().find(|d| d.id() == 0x02).unwrap();
792
793        assert_eq!(dialect.name(), "Swiss");
794        assert_eq!(dialect.id(), 0x02);
795    }
796
797    #[test]
798    fn test_hid_country_code_from_id() {
799        let hid_country_code = HidCountryCode::from_id(0x29).unwrap();
800
801        assert_eq!(hid_country_code.name(), "Switzerland");
802        assert_eq!(hid_country_code.id(), 0x29);
803
804        let hid_country_code = HidCountryCode::from_id(0x00).unwrap();
805        assert_eq!(hid_country_code.name(), "Not supported");
806    }
807
808    #[test]
809    fn test_video_terminal_from_id() {
810        let video_terminal = VideoTerminal::from_id(0x0100).unwrap();
811
812        assert_eq!(video_terminal.name(), "USB Vendor Specific");
813        assert_eq!(video_terminal.id(), 0x0100);
814
815        let video_terminal = VideoTerminal::from_id(0x0403).unwrap();
816        assert_eq!(video_terminal.name(), "Component Video");
817    }
818}