hid-types 0.2.1

Rust types for working with USB HID report descriptors
Documentation
//! A utility for handling well-known IDs and reserved values.

/// A container that holds either a known variant, or an integer value with unknown meaning.
///
/// # Example
///
/// ```rust
/// #[repr(u8)]
/// enum KnownColor {
///     Red = 0,
///     Green = 1,
///     Blue = 2,
/// }
///
/// // This can be autogenerated using something like `num_enum::TryFromPrimitive`.
/// impl TryFrom<u8> for KnownColor {
///     type Error = Unrecognized;
///
///     fn try_from(value: u8) -> Result<Self, Self::Error> {
///         match value {
///             0 => Ok(KnownColor::Red),
///             1 => Ok(KnownColor::Green),
///             2 => Ok(KnownColor::Blue),
///             _ => Err(Unrecognized),
///         }
///     }
/// }
/// struct Unrecognized;
///
/// # use hid_types::Ident;
/// type Color = Ident<KnownColor, u8>;
///
/// let green = Color::from_integer(1);
/// assert!(matches!(green, Color::Known(KnownColor::Green)));
///
/// let orange = Color::from_integer(5);
/// assert!(matches!(orange, Color::Reserved(5)));
/// ```
///
#[derive(Clone, Copy, PartialEq)]
pub enum Ident<T, Repr> {
    /// A well-known value.
    Known(T),
    /// A reserved value.
    ///
    /// This can be used to identify vendor-specific types.
    Reserved(Repr),
}

/// Conversion from the "known" type to the `Ident::Known` variant.
impl<T, Repr> From<T> for Ident<T, Repr> {
    fn from(value: T) -> Self {
        Self::Known(value)
    }
}

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

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

    impl<T, Repr> Debug for Ident<T, Repr>
    where
        T: Clone + Copy + PartialEq + Debug,
        Repr: Clone + Copy + PartialEq + Debug + UpperHex,
    {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            match self {
                Self::Known(id) => id.fmt(f),
                Self::Reserved(id) => f
                    .debug_tuple("Reserved")
                    .field(
                        // FIXME: it would be nice to compute the number of leading zeroes
                        // for the Repr type.
                        &format_args!("{:#04X}", id),
                    )
                    .finish(),
            }
        }
    }
}

impl<T, Repr> Ident<T, Repr> {
    /// Test if the value is reserved.
    pub fn is_reserved(&self) -> bool {
        matches!(self, Self::Reserved(_))
    }
}

impl<T, Repr> Ident<T, Repr>
where
    T: TryFrom<Repr>,
    Repr: Clone + Copy,
{
    /// Try to decode a known identifier; if that fails store the raw number as a "reserved" value.
    pub fn from_integer(value: Repr) -> Self {
        match T::try_from(value) {
            Ok(id) => Self::Known(id),
            Err(_) => Self::Reserved(value),
        }
    }
}

impl<T, Repr> Ident<T, Repr>
where
    T: Into<Repr>,
{
    /// Convert the identifier to an integer.
    pub fn to_integer(self) -> Repr {
        match self {
            Ident::Known(id) => id.into(),
            Ident::Reserved(id) => id,
        }
    }
}