hid-types 0.1.1

Rust types for working with USB HID report descriptors
Documentation
//! HID descriptor items.

use bitfield_struct::bitfield;
use num_enum::{IntoPrimitive, TryFromPrimitive};

use crate::Ident;

/// A one-byte tag+type+size value
#[bitfield(u32, debug = false)]
#[derive(PartialEq)]
pub struct IoFlags {
    #[bits(1)]
    data_or_constant: bool,
    #[bits(1)]
    array_or_variable: bool,
    #[bits(1)]
    absolute_or_relative: bool,
    #[bits(1)]
    wrap: bool,
    #[bits(1)]
    non_linear: bool,
    #[bits(1)]
    no_preferred: bool,
    #[bits(1)]
    null_state: bool,
    // Note: `volatile` is only valid for Output and Feature (not Input)
    #[bits(1)]
    volatile: bool,
    #[bits(1)]
    bitfield_or_buffered: bool,
    #[bits(23)]
    reserved: u32,
}

impl IoFlags {
    /// Construct a new `IoFlags` for a data field.
    pub const fn data() -> Self {
        Self::new()
    }

    /// Construct a new `IoFlags` for a constant field.
    pub const fn constant() -> Self {
        Self::new().with_data_or_constant(true)
    }

    /// The field stores a variable (otherwise it will store an array)
    pub const fn variable(self) -> Self {
        self.with_array_or_variable(true)
    }

    /// The field holds a relative value (by default values are absolute)
    pub const fn relative(self) -> Self {
        self.with_absolute_or_relative(true)
    }
}

/// A collection of shorthand types for specifying input/output/feature item flags.
///
/// These are not required ([`IoFlags`] may be used instead) but because it may not
/// be clear what the default flags are, these values may improve readability.
pub mod ioflags {
    use super::IoFlags;

    /// Input/Output/Feature flags, using the terminology common in USB HID standards.
    ///
    /// This is shorthand for `IoFlags::data().variable()`.
    pub const DATA_VARIABLE_ABSOLUTE: IoFlags = IoFlags::data().variable();

    /// Input/Output/Feature flags for a constant, using the terminology common in USB HID standards.
    ///
    /// This is shorthand for `IoFlags::constant()`. It is commonly used to declare padding bits or bytes.
    pub const CONSTANT: IoFlags = IoFlags::constant();

    /// Input/Output/Feature flags for a data array, using the terminology common in USB HID standards.
    ///
    /// This is shorthand for `IoFlags::data()` (Note: the flag defaults to "array").
    pub const ARRAY: IoFlags = IoFlags::data();
}

/// A wrapper indicating `IoFlags` that should be interpreted as Input flags.
#[derive(Clone, Copy, PartialEq)]
pub struct InputFlags(pub IoFlags);

/// A wrapper indicating `IoFlags` that should be interpreted as Output or Feature flags.
#[derive(Clone, Copy, PartialEq)]
pub struct OutputFeatureFlags(pub IoFlags);

/// A known __Collection__ type.
#[expect(missing_docs)]
#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)]
#[repr(u8)]
pub enum KnownCollectionType {
    Physical = 0,
    Application = 1,
    Logical = 2,
    Report = 3,
    NamedArray = 4,
    UsageSwitch = 5,
    UsageModifier = 6,
}

/// A __Collection__ type.
pub type CollectionType = Ident<KnownCollectionType, u8>;

/// Some known unit values.
///
/// Note: this type is incomplete, and does not represent all
/// the possible unit values.
#[expect(missing_docs)]
#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive)]
#[repr(u32)]
#[non_exhaustive]
pub enum KnownUnit {
    // Length^1 in SI Linear (1)
    Centimeter = 0x11,
    // Length^1 in SI Rotation (2)
    Radian = 0x12,
    // Length^1 in English Linear (3)
    Inch = 0x13,
    // Length^1 in English Rotation(4)
    Degree = 0x14,
}

/// A __Unit__ value.
pub type Unit = Ident<KnownUnit, u32>;

#[cfg(feature = "std")]
mod std_impls {
    use super::*;

    use std::fmt::{self, Debug};

    fn debug_flags(is_input: bool, flags: IoFlags, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if flags.data_or_constant() {
            f.write_str("Constant ")?;
        } else {
            f.write_str("Data ")?;
        }
        if flags.array_or_variable() {
            f.write_str("Variable ")?;
        } else {
            f.write_str("Array ")?;
        }
        if flags.absolute_or_relative() {
            f.write_str("Relative ")?;
        } else {
            f.write_str("Absolute ")?;
        }
        if flags.wrap() {
            f.write_str("Wrap ")?;
        } else {
            f.write_str("No-Wrap ")?;
        }
        if flags.non_linear() {
            f.write_str("Non-Linear ")?;
        } else {
            f.write_str("Linear ")?;
        }
        if flags.no_preferred() {
            f.write_str("No-Preferred ")?;
        } else {
            f.write_str("Preferred-State ")?;
        }
        if flags.null_state() {
            f.write_str("Null-State ")?
        } else {
            f.write_str("No-Null-Position ")?;
        }

        // The `volatile` field is only valid for Output and Feature tags.
        if flags.volatile() {
            f.write_str("Volatile ")?;
        } else {
            if !is_input {
                f.write_str("Non-Volatile ")?;
            }
        }

        if flags.bitfield_or_buffered() {
            f.write_str("Buffered-Bytes")?;
        } else {
            f.write_str("Bit-Field")?;
        }
        Ok(())
    }

    impl Debug for InputFlags {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            debug_flags(true, self.0, f)
        }
    }

    impl Debug for OutputFeatureFlags {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            debug_flags(false, self.0, f)
        }
    }
}