hid-types 0.1.1

Rust types for working with USB HID report descriptors
Documentation
//! HID Report Descriptor Items
//!
//! This module contains fully populated descriptor items (containing values).
//! See the [`hid`][crate::hid] module for just the item ID values.

use crate::hid::{CollectionType, InputFlags, OutputFeatureFlags, Unit};
use crate::id::usage::UsagePage;
use crate::item::usage::{ExtendedUsage, Usage};

pub mod usage;

/// An HID report descriptor Item.
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum Item {
    /// An undecoded "long" item.
    ///
    /// According to the spec
    /// ([Device Class Definition for Human Interface Devices](https://www.usb.org/document-library/device-class-definition-hid-111),
    /// section 6.2.2.3), there are no standard-defined uses for this encoding.
    Long(LongItem),
    /// A __Main__ item.
    ///
    /// See [Device Class Definition for Human Interface Devices](https://www.usb.org/document-library/device-class-definition-hid-111),
    /// section 6.2.2.4.
    Main(Main),
    /// A __Global__ item.
    Global(Global),
    /// A __Local__ item.
    Local(Local),
    /// A reserved item value.
    ///
    /// This should not be used; the descriptor cannot be properly decoded.
    Reserved(u8),
}

impl Item {
    #[cfg(feature = "std")]
    /// A string identifying the type of the item.
    pub fn type_note(&self) -> &'static str {
        match self {
            Item::Long(_) => "[long]",
            Item::Main(_) => "[main]",
            Item::Global(_) => "[global]",
            Item::Local(_) => "[local]",
            Item::Reserved(_) => "[reserved]",
        }
    }
}

/// A __Main__ item.
///
/// See [Device Class Definition for Human Interface Devices](https://www.usb.org/document-library/device-class-definition-hid-111),
/// section 6.2.2.4.
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum Main {
    /// An __Input__ item.
    Input(InputFlags),
    /// An __Output__ item.
    Output(OutputFeatureFlags),
    /// A __Feature__ item.
    Feature(OutputFeatureFlags),
    /// A __Collection__ item.
    Collection(CollectionType),
    /// An __End Collection__ item.
    EndCollection,
    /// A reserved item type.
    Reserved(u8),
}

/// A __Global__ item.
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum Global {
    /// A __Usage Page__ item.
    ///
    /// The Usage Page is a 16-bit value that identifies a specific table of Usage ID values.
    /// Future __Usage__ items will select a Usage ID from that Usage Page table.
    UsagePage(UsagePage),
    /// A __Logical Minimum__ item.
    LogicalMinimum(i32),
    /// A __Logical Maximum__ item.
    LogicalMaximum(i32),
    /// A __Physical Minimum__ item.
    PhysicalMinimum(i32),
    /// A __Physical Maximum__ item.
    PhysicalMaximum(i32),
    /// A __Unit Exponent__ item.
    UnitExponent(i32),
    /// A __Unit__ item.
    Unit(Unit),
    /// A __Report Size__ item.
    ReportSize(u32),
    /// A __Report ID__ item.
    ReportId(u32),
    /// A __Report Count__ item.
    ReportCount(u32),
    /// A __Push__ item.
    Push,
    /// A __Pop__ item.
    Pop,
    /// A reserved item type.
    Reserved(u8),
}

/// A __Local__ item.
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum Local {
    /// A __Usage__ item.
    Usage(Usage),
    /// A __Usage__ item, that also contains a __Usage Page__.
    ExtendedUsage(ExtendedUsage),
    /// A __Usage Minimum__ item.
    UsageMinimum(u32),
    /// A __Usage Maximum__ item.
    UsageMaximum(u32),
    /// A __Designator Index__ item.
    DesignatorIndex(u32),
    /// A __Designator Minimum__ item.
    DesignatorMinimum(u32),
    /// A __Designator Maximum__ item.
    DesignatorMaximum(u32),
    /// A __String Index__ item.
    StringIndex(u32),
    /// A __String Minimum__ item.
    StringMinimum(u32),
    /// A __String Maximum__ item.
    StringMaximum(u32),
    /// A __Delimiter__ item.
    Delimiter(bool),
    /// A reserved type.
    Reserved(u8),
}

/// An item using the "long" encoding.
///
/// According to the spec
/// ([Device Class Definition for Human Interface Devices](https://www.usb.org/document-library/device-class-definition-hid-111),
/// section 6.2.2.3), there are no standard-defined uses for this encoding.
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "std", derive(Debug))]
#[expect(missing_docs)]
pub struct LongItem {
    pub tag: u8,
    #[cfg(feature = "std")]
    pub data: std::vec::Vec<u8>,
}

#[cfg(feature = "std")]
mod std_impls {
    use std::fmt::{self, Display};

    use super::*;

    impl Display for Item {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            match self {
                Item::Long(item) => {
                    write!(f, "tag {:#04x} [{} bytes]", item.tag, item.data.len())
                }
                Item::Main(item) => {
                    write!(f, "{item}")
                }
                Item::Global(item) => {
                    write!(f, "{item}")
                }
                Item::Local(item) => write!(f, "{item}"),
                Item::Reserved(ty) => write!(f, "reserved type {ty}"),
            }
        }
    }

    impl Display for Main {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            match self {
                Main::Input(flags) => {
                    write!(f, "Input: {flags:?}")
                }
                Main::Output(flags) => {
                    write!(f, "Output: {flags:?}")
                }
                Main::Feature(flags) => {
                    write!(f, "Feature: {flags:?}")
                }
                Main::Collection(coll) => {
                    write!(f, "Collection: {coll:?}")
                }
                Main::EndCollection => {
                    write!(f, "EndCollection")
                }
                Main::Reserved(tag) => {
                    write!(f, "Reserved tag {tag:#04X}")
                }
            }
        }
    }

    impl Display for Global {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            match self {
                Global::UsagePage(page) => {
                    write!(f, "Usage Page: {page:?}")
                }
                Global::LogicalMinimum(value) => write!(f, "Logical Minimum: {value}"),
                Global::LogicalMaximum(value) => write!(f, "Logical Maximum: {value}"),
                Global::PhysicalMinimum(value) => write!(f, "Physical Minimum: {value}"),
                Global::PhysicalMaximum(value) => write!(f, "Physical Maximum: {value}"),
                Global::UnitExponent(value) => write!(f, "Unit Exponent: {value}"),
                Global::Unit(unit) => write!(f, "Unit: {unit:?}"),
                Global::Push => write!(f, "Push"),
                Global::Pop => write!(f, "Pop"),
                Global::Reserved(tag) => write!(f, "Reserved tag {tag:#04X}"),
                Global::ReportSize(value) => write!(f, "Report Size: {value}"),
                Global::ReportId(value) => write!(f, "Report ID: {value}"),
                Global::ReportCount(value) => write!(f, "Report Count: {value}"),
            }
        }
    }

    impl Display for Local {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            match self {
                Local::Usage(usage) => {
                    write!(f, "Usage: {usage}")
                }
                Local::ExtendedUsage(extended_usage) => {
                    write!(f, "Extended Usage: {extended_usage:?}")
                }
                Local::UsageMinimum(value) => {
                    write!(f, "Usage Minimum: {value}")
                }
                Local::UsageMaximum(value) => {
                    write!(f, "Usage Maximum: {value}")
                }
                Local::DesignatorIndex(value) => write!(f, "Designator Index: {value}"),
                Local::DesignatorMinimum(value) => write!(f, "Designator Minimum: {value}"),
                Local::DesignatorMaximum(value) => write!(f, "Designator Maximum: {value}"),
                Local::StringIndex(value) => write!(f, "String Index: {value}"),
                Local::StringMinimum(value) => write!(f, "String Minimum: {value}"),
                Local::StringMaximum(value) => write!(f, "String Maximum: {value}"),
                Local::Delimiter(value) => write!(f, "Delimiter: {value}"),
                Local::Reserved(tag) => write!(f, "Reserved tag {tag:#04X}"),
            }
        }
    }
}