// THIS FILE IS GENERATED, DO NOT EDIT
{# Ignore the warning above, you're looking at the source file #}
//! A wrapper around the [HID Usage Tables (HUT)](https://usb.org/document-library/hid-usage-tables-15).
//!
//! In this document and unless stated otherwise, a reference to
//! - **"HID Section a.b.c"** refers to the [HID Device Class Definition for HID 1.11](https://www.usb.org/document-library/device-class-definition-hid-111)
//! - **"HUT Section a.b.c"** refers to the
//! [HID Usage Tables (HUT) version 1.5](https://usb.org/document-library/hid-usage-tables-15)
//!
//! This module is created through code generation from the HID Usage Tables.
//!
//! # Terminology
//!
//! See HID Section 5.5: a HID Usage is a 32 bit value comprising of a 16-bit Usage
//! Page (MSB) and a 16-bit Usage ID (LSB) so that:
//!
//! ```
//! # let usage_page: u32 = 0;
//! # let usage_id: u32 = 0;
//! let usage: u32 = (usage_page << 16) | usage_id;
//! ```
//! Note that the HID encoding requires little endian byte order on the wire.
//!
//! In this module:
//!
//! - **"Usage Page"** refers to the 16-bit value. Where the Usage Page is converted
//! to or from a 32-bit value the Usage Page is in the upper 16 bits of that value and
//! the lower 16 bits are ignored or set to zero.
//! ```
//! # let usage: u32 = 0;
//! let usage_page: u16 = (usage >> 16) as u16 & 0xffff;
//! ```
//! - **"Usage ID"** refers to the 16-bit value. Where the Usage ID is converted to
//! or from a 32-bit value the Usage is in the lower 16 bits of that value and
//! the upper 16 bits are ignored or set to zero.
//! ```
//! # let usage: u32 = 0;
//! let usage_id: u16 = (usage & 0xffff) as u16;
//! ```
//! - **"Usage"** refers to the 32-bit value comprising a Usage Page and a Usage.
//!
//! # Converting between types
//!
//! All defined [Usages](Usage) and [UsagePages](UsagePage) implement [AsUsagePage] and (if applicable) [AsUsage] as
//! well as the [`From<u16>`](From), [`From<u32>`](From), [`TryFrom<u16>`](TryFrom), and [`TryFrom<u32>`](TryFrom)
//! conversions so that:
//! ```
//! # use hut::*;
//! let usage_page_value: u16 = 0x01; // Generic Desktop
//! let usage_id_value: u16 = 0x02; // Mouse
//! let usage_value: u32 = ((usage_page_value as u32) << 16) | usage_id_value as u32;
//!
//! // Create a known Usage from a 32-bit value
//! let u: Usage = Usage::try_from(usage_value).unwrap();
//! assert!(matches!(u, Usage::GenericDesktop(GenericDesktop::Mouse)));
//!
//! // Create a known Usage from the Usage Page and Usage ID values
//! let u2 = Usage::new_from_page_and_id(usage_page_value, usage_id_value).unwrap();
//! assert_eq!(u, u2);
//!
//! // Create a known Usage from an individual Usage Page enum item
//! let u3 = Usage::from(GenericDesktop::Mouse);
//! assert_eq!(u, u3);
//!
//! // Create a known Usage from an known Usage Page enum item
//! let gd_mouse = GenericDesktop::try_from(usage_id_value).unwrap();
//! let u4 = Usage::from(gd_mouse);
//! assert_eq!(u, u4);
//!
//! // Convert to and fro the Usage either via u32 or the AsUsage trait
//! let u = GenericDesktop::Mouse;
//! assert_eq!(u32::from(&u), usage_value);
//! assert_eq!(u.usage_value(), usage_value);
//!
//! // Extract the 16-bit Usage ID either via u16 or the AsUsage trait
//! assert_eq!(u16::from(&u), usage_id_value);
//! assert_eq!(u.usage_id_value(), usage_id_value);
//!
//! // Extract the Usage Page from the Usage enum value
//! let up = u.usage_page();
//! assert!(matches!(up, UsagePage::GenericDesktop));
//! let up: UsagePage = UsagePage::from(&u);
//! assert!(matches!(up, UsagePage::GenericDesktop));
//!
//! // Get the Usage Page numeric value is via the AsUsagePage
//! assert_eq!(u16::from(&up), usage_page_value);
//! assert_eq!(up.usage_page_value(), usage_page_value);
//! ```
//!
//! Naming Usages (e.g. [`GenericDesktop::Mouse`]) above works for Defined Usage
//! Pages, Generated Usage Pages (see below) need to be destructured via their
//! individual elements:
//! ```
//! # use hut::*;
//! let usage_page_value: u16 = 0x09; // Button
//! let usage_id_value: u16 = 8; // Button number 8
//! let usage_value: u32 = ((usage_page_value as u32) << 16) | usage_id_value as u32;
//!
//! let u = Usage::try_from(usage_value).unwrap();
//! let button = Usage::Button(Button::Button(8));
//! assert!(matches!(Usage::try_from(usage_value).unwrap(), button));
//! // or via from() or into()
//! let button: Usage = Button::Button(8).into();
//! assert!(matches!(Usage::try_from(usage_value).unwrap(), button));
//! ```
//! Once a Usage is created, the [AsUsagePage] and [AsUsage] traits and conversion to and from
//! [u16] and [u32] work the same as for a Defined Usage Page.
//!
//! # Names of Usage Pages and Usage IDs
//!
//! All defined [Usages](Usage) and [UsagePages](UsagePage) implement `name()` to return a string
//! representing that page or usage:
//!
//! ```
//! # use hut::*;
//! let up = UsagePage::GenericDesktop;
//! assert_eq!(up.name(), "Generic Desktop");
//! let up = UsagePage::SimulationControls;
//! assert_eq!(up.name(), "Simulation Controls");
//!
//! let usage = GenericDesktop::Mouse;
//! assert_eq!(usage.name(), "Mouse");
//! let usage = SimulationControls::CyclicControl;
//! assert_eq!(usage.name(), "Cyclic Control");
//! ```
//!
//! # Generated Usage Pages
//!
//! The HUT differ between "Defined" and "Generated" Usage Pages. The former define Usage ID values
//! and their meanings, the latter define a Usage ID range, with the actual Usage ID simply
//! referring to "nth thing in this usage page". One example for this is the Button Usage Page
//! (0x09) where a Usage ID of 3 means "Button 3".
//!
//! ```
//! # use hut::*;
//! let b = Button::Button(3);
//! let o = Ordinal::Ordinal(23);
//! ```
//!
//! Unlike Defined Usage Pages these Generated Usage Pages need to be destructured in `match`
//! statements:
//!
//! ```
//! # use hut::*;
//! let b = Button::Button(3);
//! match b {
//! Button::Button(b) => println!("Button {b}"),
//! _ => {},
//! }
//! ```
//!
//! The following usage pages are Generated:
{% for usage_page in usage_pages %}
{% if usage_page.usage_page_type == "Generated" %}
//! - Usage Page 0x{{ '%X' % usage_page.value }} - [{{usage_page.name}}]
{% endif %}
{% endfor %}
//!
//! A further special case of this is the [Unicode] usage page which is not in the HUT
//! document and was inserted during code generation.
//!
//! # Vendor Defined Usage Pages (0xFF00 to 0xFFFF)
//!
//! [Vendor Defined Usage Pages](VendorDefinedPage) and [VendorUsages](VendorDefinedPage::VendorUsage) are not autogenerated and thus
//! follow a different approach: the Usage inside the Usage Page is a simple
//! numeric usage that needs to be destructured in `match` statements.
//!
//! ```
//! # use hut::*;
//! let v = Usage::VendorDefinedPage {
//! vendor_page: VendorPage::try_from(0xff00 as u16).unwrap(),
//! usage: VendorDefinedPage::VendorUsage { usage_id: 0x01 },
//! };
//! match v {
//! Usage::VendorDefinedPage {
//! vendor_page,
//! usage,
//! } => println!("Vendor Usage ID {usage}"),
//! _ => {},
//! }
//! ```
//!
//! A notable exception is the [Wacom] (`0xFF0D`) which is technically a
//! Vendor-defined page but with defined Usages. Converting from a [UsagePage]
//! or [Usage] numeric value will produce the correct or [Wacom] Usage, not a [VendorDefinedPage::VendorUsage].
//!
//! # Reserved Usage Pages
//!
//! [Reserved Usage Pages](ReservedUsagePage) and [ReservedUsages](ReservedUsagePage::ReservedUsage) are
//! not autogenerated and thus follow a different approach: the Usage inside the Usage Page is a simple
//! numeric usage that needs to be destructured in `match` statements.
//!
//! Unlike the [Vendor Defined Usage Pages](VendorDefinedPage) a [Reserved Usage Page](ReservedPage) may become
//! a defined page in a later version of the HUT standard and thus in a future version of this crate.
//! A caller must not rely on a Reserved Usage Page or Reserved Usage to remain so.
//!
//! The following Usage Pages are reserved as of HUT 1.5 (see HUT Section 3, p15):
//! - `0x13`, `0x15-0x1F`
//! - `0x21-0x3F`
//! - `0x42-0x58`
//! - `0x5A-0x7F`
//! - `0x83-0x83`
//! - `0x86-0x8B`
//! - `0x8F-0x8F`
//! - `0x93-0xF1CF`
//! - `0xF1D1-0xFEFF`
//!
//! # Renames
//!
//! For technical reasons, spaces, (` `), dashes (`-`), and slashes (`/`) are
//! stripped out of Usage Page and Usage names. The string representation via
//! the `Display` trait will have the unmodified value.
#![allow(clippy::identity_op, clippy::eq_op, clippy::match_single_binding)]
#![no_std]
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "std")]
use std::{fmt, format, string::String, string::ToString};
use core::ops::BitOr;
/// Error raised if conversion between HUT elements fails.
#[derive(Debug)]
pub enum HutError {
/// The usage page value is not known. Usage Pages
/// may get added in the future and a future version
/// of this crate may not raise this error for the same value.
UnknownUsagePage { usage_page: u16 },
/// The usage ID value is not known. Usage IDs
/// may get added in the future and a future version
/// of this crate may not raise this error for the same value.
UnknownUsageId { usage_id: u16 },
/// The value given for a [VendorDefinedPage] is outside the allowed range.
InvalidVendorPage { vendor_page: u16 },
/// The value given for a [ReservedUsagePage] is outside the allowed range.
InvalidReservedPage { reserved_page: u16 },
/// The 32-bit usage value given cannot be resolved. Usages
/// may get added in the future and a future version
/// of this crate may not raise this error for the same value.
UnknownUsage,
}
impl core::error::Error for HutError { }
impl core::fmt::Display for HutError {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
HutError::UnknownUsagePage { usage_page } => write!(fmt, "Unknown Usage Page {}", usage_page),
HutError::UnknownUsageId { usage_id } => write!(fmt, "Unknown Usage ID {}", usage_id),
HutError::InvalidVendorPage { vendor_page } => write!(fmt, "Invalid Vendor Page {}", vendor_page),
HutError::InvalidReservedPage {reserved_page } => write!(fmt, "Invalid Reserved Page {}", reserved_page),
HutError::UnknownUsage => write!(fmt, "Unknown Usage"),
}
}
}
type Result<T> = core::result::Result<T, HutError>;
/// A trait to return the Usage and Usage ID as numeric value
pub trait AsUsage {
/// Returns the 32-bit Usage numeric value of this Usage
fn usage_value(&self) -> u32;
/// Returns the 16-bit Usage Id numeric value of this Usage
fn usage_id_value(&self) -> u16;
/// Returns this usage as [Usage]
fn usage(&self) -> Usage;
}
/// A trait to return the Usage Page as numeric value
pub trait AsUsagePage {
/// Returns the 16-bit Usage Page value
fn usage_page_value(&self) -> u16;
/// Returns the [UsagePage]
fn usage_page(&self) -> UsagePage;
}
/// A HID UsagePage, see HID Section 5.5. This represents the upper 16 bits in the
/// 32-bit Usage. Where a [UsagePage] is converted to or from 32 bit, the
/// [UsagePage] value are the upper 16 bits only and the lower 16 bits are
/// ignored or set to zero.
/// ```
/// # use hut::*;
/// let usage = Usage::from(GenericDesktop::Mouse);
/// let up: UsagePage = UsagePage::from(&usage);
/// assert!(matches!(up, UsagePage::GenericDesktop));
/// ```
/// Note: this enum is generated from the HUT specification.
#[allow(non_camel_case_types)]
#[derive(Debug)]
#[non_exhaustive]
pub enum UsagePage {
{% for usage_page in usage_pages %}
/// Usage Page `0x{{ '%X' % usage_page.value }}`: "{{usage_page.printable}}",
/// see [{{usage_page.name}}].
{{usage_page.name}},
{% endfor %}
/// The Reserved Usage Pages (range: various). See [ReservedUsagePage].
ReservedUsagePage(ReservedPage),
/// The Vendor Defined Pages, range `0xFF00 - 0xFFFF`. See [VendorDefinedPage].
VendorDefinedPage(VendorPage),
}
/// Represents a Reserved Page number value of in the current range of
/// reserved values. See [ReservedUsagePage].
///
/// ```
/// # use hut::*;
/// // 0x3f is currently reserved
/// let vp: ReservedPage = ReservedPage::try_from(0x3f as u16).unwrap();
/// let vp: ReservedPage = ReservedPage::try_from(0x003f1234 as u32).unwrap();
///
/// let usage = Usage::try_from(0x003f1234).unwrap();
/// let up = UsagePage::from(&usage);
/// assert!(matches!(up, UsagePage::ReservedUsagePage(_)));
/// ```
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct ReservedPage(u16);
impl From<&ReservedPage> for ReservedPage {
fn from(v: &ReservedPage) -> ReservedPage {
ReservedPage(v.0)
}
}
impl From<&ReservedPage> for u16 {
fn from(v: &ReservedPage) -> u16 {
v.0
}
}
impl From<ReservedPage> for u16 {
fn from(v: ReservedPage) -> u16 {
u16::from(&v)
}
}
impl From<&ReservedPage> for u32 {
fn from(v: &ReservedPage) -> u32 {
(v.0 as u32) << 16
}
}
impl From<ReservedPage> for u32 {
fn from(v: ReservedPage) -> u32 {
u32::from(&v)
}
}
impl TryFrom<u16> for ReservedPage {
type Error = HutError;
fn try_from(v: u16) -> Result<ReservedPage> {
match v {
p @ 0x13 => Ok(ReservedPage(p)),
p @ 0x15..=0x1F => Ok(ReservedPage(p)),
p @ 0x21..=0x3F => Ok(ReservedPage(p)),
p @ 0x42..=0x58 => Ok(ReservedPage(p)),
p @ 0x5A..=0x7F => Ok(ReservedPage(p)),
p @ 0x83..=0x83 => Ok(ReservedPage(p)),
p @ 0x86..=0x8B => Ok(ReservedPage(p)),
p @ 0x8F..=0x8F => Ok(ReservedPage(p)),
p @ 0x93..=0xF1CF => Ok(ReservedPage(p)),
p @ 0xF1D1..=0xFEFF => Ok(ReservedPage(p)),
n => Err(HutError::InvalidReservedPage { reserved_page: n }),
}
}
}
impl TryFrom<u32> for ReservedPage {
type Error = HutError;
fn try_from(v: u32) -> Result<ReservedPage> {
ReservedPage::try_from((v >> 16) as u16)
}
}
/// Represents a Vendor Defined Page number value of in the range
/// `0xFF00..=0xFFFF`. See [VendorDefinedPage].
///
/// ```
/// # use hut::*;
/// // The value has to be 0xff00..=0xffff
/// let vp: VendorPage = VendorPage::try_from(0xff00 as u16).unwrap();
/// let vp: VendorPage = VendorPage::try_from(0xff001234 as u32).unwrap();
///
/// let usage = Usage::try_from(0xff001234).unwrap();
/// let up = UsagePage::from(&usage);
/// assert!(matches!(up, UsagePage::VendorDefinedPage(_)));
/// ```
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct VendorPage(u16);
impl From<&VendorPage> for VendorPage {
fn from(v: &VendorPage) -> VendorPage {
VendorPage(v.0)
}
}
impl From<&VendorPage> for u16 {
fn from(v: &VendorPage) -> u16 {
v.0
}
}
impl From<VendorPage> for u16 {
fn from(v: VendorPage) -> u16 {
u16::from(&v)
}
}
impl From<&VendorPage> for u32 {
fn from(v: &VendorPage) -> u32 {
(v.0 as u32) << 16
}
}
impl From<VendorPage> for u32 {
fn from(v: VendorPage) -> u32 {
u32::from(&v)
}
}
impl TryFrom<u16> for VendorPage {
type Error = HutError;
fn try_from(v: u16) -> Result<VendorPage> {
match v {
p @ 0xff00..=0xffff => Ok(VendorPage(p)),
n => Err(HutError::InvalidVendorPage { vendor_page: n }),
}
}
}
impl TryFrom<u32> for VendorPage {
type Error = HutError;
fn try_from(v: u32) -> Result<VendorPage> {
VendorPage::try_from((v >> 16) as u16)
}
}
impl UsagePage {
/// Returns the Usage Page for the given Usage Page value. This is the
/// 16-bit Usage Page value only, not the full 32-bit Usage.
/// ```
/// # use hut::*;
/// let usage_value: u16 = 0x1; // GenericDesktop
/// let usage_page = UsagePage::from_usage_page_value(usage_value).unwrap();
/// assert!(matches!(UsagePage::GenericDesktop, usage_page));
/// ```
pub fn from_usage_page_value(usage_page: u16) -> Result<UsagePage> {
UsagePage::try_from(usage_page)
}
/// Returns the Usage Page for the given Usage numeric value. The Usage Page
/// must be in the upper 16 bits of the `usage` value and the lower 16 bits
/// are ignored.
/// ```
/// # use hut::*;
/// let usage_value: u32 = (0x1 << 16) | 0x2;
/// let usage = UsagePage::from_usage_value(usage_value).unwrap();
/// assert!(matches!(Usage::from(GenericDesktop::Mouse), usage));
/// ```
pub fn from_usage_value(usage: u32) -> Result<UsagePage> {
let up: u16 = (usage >> 16) as u16;
UsagePage::try_from(up)
}
/// Returns the 32-bit Usage that is this Usage Page combined with
/// the 16 bits Usage ID.
///
/// ```
/// # use hut::*;
/// let up = UsagePage::GenericDesktop;
/// let usage_id_value: u16 = 0x02; // Mouse
///
/// let usage = up.to_usage_from_value(usage_id_value).unwrap();
/// assert!(matches!(Usage::from(GenericDesktop::Mouse), usage));
/// ```
pub fn to_usage_from_value(&self, usage: u16) -> Result<Usage> {
let up: u32 = (self.usage_page_value() as u32) << 16;
let u: u32 = usage as u32;
Usage::try_from(up | u)
}
/// Return a printable name for this usage page
/// ```
/// # use hut::*;
/// let up = UsagePage::GenericDesktop;
/// assert_eq!(up.name(), "Generic Desktop");
/// ```
#[cfg(feature = "std")]
pub fn name(&self) -> String {
match self {
{% for usage_page in usage_pages %}
UsagePage::{{usage_page.name}} => "{{usage_page.printable}}".into(),
{% endfor %}
UsagePage::ReservedUsagePage(reserved_page) => {
format!("Reserved Usage Page {:04X}", u16::from(reserved_page))
}
UsagePage::VendorDefinedPage(vendor_page) => {
format!("Vendor Defined Page {:04X}", u16::from(vendor_page))
}
}
}
}
impl AsUsagePage for UsagePage {
/// Returns the 16 bit Usage Page value of this Usage Page
/// ```
/// # use hut::*;
/// let usage_value: u16 = 0x1; // GenericDesktop
/// let usage_page = UsagePage::from_usage_page_value(usage_value).unwrap();
/// assert_eq!(usage_page.usage_page_value(), 0x1);
/// ```
fn usage_page_value(&self) -> u16 {
u16::from(self)
}
/// Returns our current [UsagePage]
/// ```
/// # use hut::*;
/// let usage_value: u16 = 0x1; // GenericDesktop
/// let usage_page = UsagePage::from_usage_page_value(usage_value).unwrap();
/// assert!(matches!(usage_page.usage_page(), UsagePage::GenericDesktop));
/// ```
/// There is seldom a need to invoke this function, it is merely
/// implemented to meet the [AsUsagePage] requirements.
fn usage_page(&self) -> UsagePage {
UsagePage::try_from(u16::from(self)).unwrap()
}
}
{% for usage_page in usage_pages %}
{% if usage_page.usage_page_type == "Defined" %}
/// *Usage Page `0x{{ '%X' % usage_page.value }}`: "{{usage_page.printable}}"*
///
/// **This enum is autogenerated from the HID Usage Tables**.
{% if usage_page.usages | length() >= 2 %}
/// ```
/// # use hut::*;
/// let u1 = Usage::{{usage_page.name}}({{usage_page.name}}::{{usage_page.usages[1].name}});
/// let u2 = Usage::new_from_page_and_id(0x{{'%X' % usage_page.value}}, 0x{{'%X' % usage_page.usages[1].value}}).unwrap();
/// let u3 = Usage::from({{usage_page.name}}::{{usage_page.usages[1].name}});
/// let u4: Usage = {{usage_page.name}}::{{usage_page.usages[1].name}}.into();
/// assert_eq!(u1, u2);
/// assert_eq!(u1, u3);
/// assert_eq!(u1, u4);
///
/// assert!(matches!(u1.usage_page(), UsagePage::{{usage_page.name}}));
/// assert_eq!(0x{{'%X' % usage_page.value}}, u1.usage_page_value());
/// assert_eq!(0x{{'%X' % usage_page.usages[1].value}}, u1.usage_id_value());
/// assert_eq!((0x{{'%X' % usage_page.value}} << 16) | 0x{{'%X' % usage_page.usages[1].value}}, u1.usage_value());
/// assert_eq!("{{usage_page.usages[1].printable}}", u1.name());
/// ```
{% endif %}
///
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[repr(u16)]
pub enum {{usage_page.name}} {
{% for usage in usage_page.usages %}
/// Usage ID `0x{{ '%X' % usage.value}}`: "{{usage.printable}}"
{{ usage.name }} = 0x{{ '%X' % usage.value}},
{% endfor %}
}
impl {{usage_page.name}} {
#[cfg(feature = "std")]
pub fn name(&self) -> String {
match self {
{% for usage in usage_page.usages %}
{% if (usage_page.name + usage.name + usage.printable)|length < 80 %}
{{usage_page.name}}::{{usage.name}} => "{{usage.printable}}",
{% else %}
{{usage_page.name}}::{{usage.name}} => {
"{{usage.printable}}"
}
{% endif %}
{% else %}
_ => "",
{% endfor %}
}
.into()
}
}
#[cfg(feature = "std")]
impl fmt::Display for {{usage_page.name}} {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name())
}
}
{% endif %}{# if usage_page.usage_page_type == Defined ... #}
{% if usage_page.usage_page_type == "Generated" %}
/// *Usage Page `0x{{ '%X' % usage_page.value }}`: "{{usage_page.printable}}"*
///
/// **This enum is autogenerated from the HID Usage Tables**.
///
/// This Usage Page is generated, not defined, any Usage IDs in this Usage
/// Page are simply the {{usage_page.name_prefix|lower}} number.
///
/// ```
/// # use hut::*;
/// let u1 = Usage::{{usage_page.name}}({{usage_page.name}}::{{usage_page.name}}(3));
/// let u2 = Usage::new_from_page_and_id(0x{{'%X' % usage_page.value}}, 3).unwrap();
/// let u3 = Usage::from({{usage_page.name}}::{{usage_page.name}}(3));
/// let u4: Usage = {{usage_page.name}}::{{usage_page.name}}(3).into();
/// assert_eq!(u1, u2);
/// assert_eq!(u1, u3);
/// assert_eq!(u1, u4);
///
/// assert!(matches!(u1.usage_page(), UsagePage::{{usage_page.name}}));
/// assert_eq!(0x{{'%X' % usage_page.value}}, u1.usage_page_value());
/// assert_eq!(3, u1.usage_id_value());
/// assert_eq!((0x{{'%X' % usage_page.value}} << 16) | 3, u1.usage_value());
/// assert_eq!("{{usage_page.name_prefix}} 3", u1.name());
/// ```
#[allow(non_camel_case_types)]
#[derive(Debug)]
#[non_exhaustive]
pub enum {{usage_page.name}} {
{{usage_page.name}}(u16),
}
impl {{usage_page.name}} {
#[cfg(feature = "std")]
pub fn name(&self) -> String {
match self {
{{usage_page.name}}::{{usage_page.name}}({{usage_page.name_prefix|lower}}) => format!("{{usage_page.name_prefix}} { {{-usage_page.name_prefix|lower-}} }"),
}
}
}
#[cfg(feature = "std")]
impl fmt::Display for {{usage_page.name}} {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name())
}
}
{% endif %}{# if usage_page.usage_page_type == Generated ... -#}
impl AsUsage for {{usage_page.name}} {
/// Returns the 32 bit Usage value of this Usage
fn usage_value(&self) -> u32 {
u32::from(self)
}
/// Returns the 16 bit Usage ID value of this Usage
fn usage_id_value(&self) -> u16 {
u16::from(self)
}
/// Returns this usage as [Usage::{{usage_page.name}}(self)](Usage::{{usage_page.name}})
/// This is a convenience function to avoid having
/// to implement `From` for every used type in the caller.
///
/// ```
/// # use hut::*;
/// let gd_x = GenericDesktop::X;
/// let usage = Usage::from(GenericDesktop::X);
/// assert!(matches!(gd_x.usage(), usage));
/// ```
fn usage(&self) -> Usage {
Usage::from(self)
}
}
impl AsUsagePage for {{usage_page.name}} {
/// Returns the 16 bit value of this UsagePage
///
/// This value is `0x{{ '%X' % usage_page.value }}` for [{{usage_page.name}}]
fn usage_page_value(&self) -> u16 {
let up = UsagePage::from(self);
u16::from(up)
}
/// Returns [UsagePage::{{usage_page.name}}]]
fn usage_page(&self) -> UsagePage {
UsagePage::from(self)
}
}
impl From<&{{usage_page.name}}> for u16 {
fn from({{usage_page.name.lower()}}: &{{usage_page.name}}) -> u16 {
{% if usage_page.usage_page_type == "Defined" %}
*{{usage_page.name.lower()}} as u16
{% endif %}
{% if usage_page.usage_page_type == "Generated" %}
match *{{usage_page.name.lower()}} {
{{usage_page.name}}::{{usage_page.name}}({{usage_page.name_prefix|lower}}) => {{usage_page.name_prefix|lower}},
}
{% endif %}
}
}
impl From<{{usage_page.name}}> for u16 {
/// Returns the 16bit value of this usage. This is identical
/// to [{{usage_page.name}}::usage_page_value()].
fn from({{usage_page.name.lower()}}: {{usage_page.name}}) -> u16 {
u16::from(&{{usage_page.name.lower()}})
}
}
impl From<&{{usage_page.name}}> for u32 {
/// Returns the 32 bit value of this usage. This is identical
/// to [{{usage_page.name}}::usage_value()].
fn from({{usage_page.name.lower()}}: &{{usage_page.name}}) -> u32 {
let up = UsagePage::from({{usage_page.name.lower()}});
let up = (u16::from(&up) as u32) << 16;
let id = u16::from({{usage_page.name.lower()}}) as u32;
up | id
}
}
impl From<&{{usage_page.name}}> for UsagePage {
/// Always returns [UsagePage::{{usage_page.name}}] and is
/// identical to [{{usage_page.name}}::usage_page()].
fn from(_: &{{usage_page.name}}) -> UsagePage {
UsagePage::{{usage_page.name}}
}
}
impl From<{{usage_page.name}}> for UsagePage {
/// Always returns [UsagePage::{{usage_page.name}}] and is
/// identical to [{{usage_page.name}}::usage_page()].
fn from(_: {{usage_page.name}}) -> UsagePage {
UsagePage::{{usage_page.name}}
}
}
impl From<&{{usage_page.name}}> for Usage {
fn from({{usage_page.name.lower()}}: &{{usage_page.name}}) -> Usage {
Usage::try_from(u32::from({{usage_page.name.lower()}})).unwrap()
}
}
impl From<{{usage_page.name}}> for Usage {
fn from({{usage_page.name.lower()}}: {{usage_page.name}}) -> Usage {
Usage::from(&{{usage_page.name.lower()}})
}
}
impl TryFrom<u16> for {{usage_page.name}} {
type Error = HutError;
fn try_from(usage_id: u16) -> Result<{{usage_page.name}}> {
match usage_id {
{% for usage in usage_page.usages %}
{{usage.value}} => Ok({{usage_page.name}}::{{usage.name}}),
{% endfor %}
{% if usage_page.usage_page_type == "Generated" %}
n => Ok({{usage_page.name}}::{{usage_page.name}}(n)),
{% else %}
n => Err(HutError::UnknownUsageId { usage_id: n }),
{% endif %}
}
}
}
impl BitOr<u16> for {{usage_page.name}} {
type Output = Usage;
/// A convenience function to combine a Usage Page with
/// a value.
///
/// This function panics if the Usage ID value results in
/// an unknown Usage. Where error checking is required,
/// use [UsagePage::to_usage_from_value].
fn bitor(self, usage: u16) -> Usage {
let up = u16::from(self) as u32;
let u = usage as u32;
Usage::try_from(up | u).expect("Invalid Usage ID for this Usage Page")
}
}
{% endfor -%}
/// *Reserved Usage Pages*
///
/// This Usage Page has no named Usage IDs, any Usages in this Usage Page are
/// reserved implementation. In a future version of the HUT standard a reserved
/// Usage Page may become a defined Usage Page.
#[allow(non_camel_case_types)]
#[derive(Debug)]
#[non_exhaustive]
pub enum ReservedUsagePage {
Undefined,
ReservedUsage { usage_id: u16 },
}
impl ReservedUsagePage {
#[cfg(feature = "std")]
fn name(&self) -> String {
match self {
ReservedUsagePage::Undefined => "Reserved Usage Undefined".to_string(),
ReservedUsagePage::ReservedUsage { usage_id } => {
format!("Reserved Usage 0x{usage_id:02x}")
}
}
}
}
#[cfg(feature = "std")]
impl fmt::Display for ReservedUsagePage {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name())
}
}
impl From<&ReservedUsagePage> for u16 {
fn from(v: &ReservedUsagePage) -> u16 {
match v {
ReservedUsagePage::Undefined => 0x00,
ReservedUsagePage::ReservedUsage { usage_id } => *usage_id,
}
}
}
/// *Usage Page `0xFF00` to `0xFFFF`: The Vendor Defined Pages*
///
/// This Usage Page has no named Usage IDs, any Usages in this Usage Page are
/// private to a vendor implementation.
#[allow(non_camel_case_types)]
#[derive(Debug)]
#[non_exhaustive]
pub enum VendorDefinedPage {
Undefined,
VendorUsage { usage_id: u16 },
}
impl VendorDefinedPage {
#[cfg(feature = "std")]
fn name(&self) -> String {
match self {
VendorDefinedPage::Undefined => "Vendor Usage Undefined".to_string(),
VendorDefinedPage::VendorUsage { usage_id } => {
format!("Vendor Usage 0x{usage_id:02x}")
}
}
}
}
#[cfg(feature = "std")]
impl fmt::Display for VendorDefinedPage {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name())
}
}
impl From<&VendorDefinedPage> for u16 {
fn from(v: &VendorDefinedPage) -> u16 {
match v {
VendorDefinedPage::Undefined => 0x00,
VendorDefinedPage::VendorUsage { usage_id } => *usage_id,
}
}
}
impl From<&Usage> for UsagePage {
#[allow(clippy::unneeded_struct_pattern)]
fn from(usage: &Usage) -> UsagePage {
match usage {
{% for usage_page in usage_pages %}
Usage::{{usage_page.name}} { .. } => UsagePage::{{usage_page.name}},
{% endfor %}
Usage::ReservedUsagePage { reserved_page, .. } => UsagePage::ReservedUsagePage(*reserved_page),
Usage::VendorDefinedPage { vendor_page, .. } => UsagePage::VendorDefinedPage(*vendor_page),
}
}
}
impl From<&UsagePage> for u16 {
/// Returns the UsagePage as 16-bit value. This is equivalent to the
/// upper 16 bits of a full 32-bit Usage value shifted down.
#[allow(clippy::unneeded_struct_pattern)]
fn from(usage_page: &UsagePage) -> u16 {
match usage_page {
{% for usage_page in usage_pages %}
UsagePage::{{usage_page.name}} { .. } => {{usage_page.value}},
{% endfor %}
UsagePage::ReservedUsagePage(reserved_page) => u16::from(reserved_page),
UsagePage::VendorDefinedPage(vendor_page) => u16::from(vendor_page),
}
}
}
impl From<UsagePage> for u16 {
fn from(usage_page: UsagePage) -> u16 {
u16::from(&usage_page)
}
}
impl TryFrom<u16> for UsagePage {
type Error = HutError;
fn try_from(usage_page: u16) -> Result<UsagePage> {
match usage_page {
{% for usage_page in usage_pages %}
{{usage_page.value}} => Ok(UsagePage::{{usage_page.name}}),
{% endfor %}
page @ 0xff00..=0xffff => Ok(UsagePage::VendorDefinedPage(VendorPage(page))),
n => match ReservedPage::try_from(n) {
Ok(r) => Ok(UsagePage::ReservedUsagePage(r)),
Err(_) => Err(HutError::UnknownUsagePage { usage_page: n }),
},
}
}
}
impl TryFrom<u32> for UsagePage {
type Error = HutError;
fn try_from(usage_page: u32) -> Result<UsagePage> {
UsagePage::try_from((usage_page >> 16) as u16)
}
}
#[cfg(feature = "std")]
impl fmt::Display for UsagePage {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name())
}
}
/// An enum wrapping all known Usages in the HUT.
/// ```
/// # use hut::*;
/// let u1 = Usage::GenericDesktop(GenericDesktop::Mouse);
/// let u2 = Usage::new_from_page_and_id(0x01, 0x02).unwrap();
/// let u3 = Usage::from(GenericDesktop::Mouse);
/// assert_eq!(u1, u2);
/// assert_eq!(u1, u3);
///
/// assert_eq!(0x1, u1.usage_page_value());
/// assert_eq!(0x2, u1.usage_id_value());
/// assert_eq!((0x1 << 16) | 0x2, u1.usage_value());
/// ```
/// Note: this enum is generated from the HUT documents.
#[allow(non_camel_case_types)]
#[derive(Debug)]
#[non_exhaustive]
pub enum Usage {
{% for usage_page in usage_pages %}
/// "{{usage_page.printable}}"
{{usage_page.name}}({{usage_page.name}}),
{% endfor %}
ReservedUsagePage {
reserved_page: ReservedPage,
usage: ReservedUsagePage,
},
VendorDefinedPage {
vendor_page: VendorPage,
usage: VendorDefinedPage,
},
}
impl Usage {
#[cfg(feature = "std")]
pub fn new_from_page_and_id(usage_page: u16, usage_id: u16) -> Result<Usage> {
Usage::try_from(((usage_page as u32) << 16) | usage_id as u32)
}
#[cfg(feature = "std")]
pub fn name(&self) -> String {
match self {
{% for usage_page in usage_pages %}
Usage::{{usage_page.name}}(usage) => usage.name(),
{% endfor %}
Usage::ReservedUsagePage { usage, .. } => usage.name(),
Usage::VendorDefinedPage { usage, .. } => usage.name(),
}
}
}
impl AsUsage for Usage {
/// Returns the 32 bit Usage value for this usage.
fn usage_value(&self) -> u32 {
self.into()
}
/// Returns the 16-bit Usage ID value for this usage.
fn usage_id_value(&self) -> u16 {
self.into()
}
/// Returns [Self]
fn usage(&self) -> Usage {
Usage::try_from(self.usage_value()).unwrap()
}
}
impl PartialEq for Usage {
fn eq(&self, other: &Self) -> bool {
u32::from(self) == u32::from(other)
}
}
impl AsUsagePage for Usage {
fn usage_page_value(&self) -> u16 {
UsagePage::from(self).into()
}
fn usage_page(&self) -> UsagePage {
match self {
{% for usage_page in usage_pages %}
Usage::{{usage_page.name}}(_) => UsagePage::{{usage_page.name}},
{% endfor %}
Usage::ReservedUsagePage { reserved_page, .. } => UsagePage::ReservedUsagePage(*reserved_page),
Usage::VendorDefinedPage { vendor_page, .. } => UsagePage::VendorDefinedPage(*vendor_page),
}
}
}
#[cfg(feature = "std")]
impl fmt::Display for Usage {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name())
}
}
impl From<&Usage> for u16 {
fn from(usage: &Usage) -> u16 {
let u: u32 = u32::from(usage);
(u & 0xFFFF) as u16
}
}
impl From<Usage> for u16 {
fn from(usage: Usage) -> u16 {
u16::from(&usage)
}
}
impl From<&Usage> for u32 {
fn from(usage: &Usage) -> u32 {
match usage {
{% for usage_page in usage_pages %}
Usage::{{usage_page.name}}(usage) => ({{usage_page.value}} << 16) | u16::from(usage) as u32,
{% endfor %}
Usage::ReservedUsagePage {
reserved_page,
usage,
} => ((u16::from(reserved_page) as u32) << 16) | u16::from(usage) as u32,
Usage::VendorDefinedPage { vendor_page, usage } => {
((u16::from(vendor_page) as u32) << 16) | u16::from(usage) as u32
}
}
}
}
impl TryFrom<u32> for Usage {
type Error = HutError;
fn try_from(up: u32) -> Result<Usage> {
match (up >> 16, up & 0xFFFF) {
{% for usage_page in usage_pages %}
({{usage_page.value}}, n) => Ok(Usage::{{usage_page.name}}(
{{usage_page.name}}::try_from(n as u16)?
)),
{% endfor %}
(p @ 0xff00..=0xffff, n) => Ok(Usage::VendorDefinedPage {
vendor_page: VendorPage(p as u16),
usage: VendorDefinedPage::VendorUsage { usage_id: n as u16 },
}),
(p, n) => match ReservedPage::try_from(p as u16) {
Ok(r) => Ok(Usage::ReservedUsagePage {
reserved_page: r,
usage: ReservedUsagePage::ReservedUsage { usage_id: n as u16 },
}),
_ => Err(HutError::UnknownUsage),
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn conversions() {
let hid_usage_page: u16 = 0x01; // Generic Desktop
let hid_usage_id: u16 = 0x02; // Mouse
let hid_usage: u32 = ((hid_usage_page as u32) << 16) | hid_usage_id as u32;
let u = GenericDesktop::Mouse;
// 32 bit usage to enum
assert!(matches!(
Usage::try_from(hid_usage).unwrap(),
Usage::GenericDesktop(_)
));
// Usage to u32
assert_eq!(u32::from(&u), hid_usage);
assert_eq!(u.usage_value(), hid_usage);
// Usage to u16 usage_id
assert_eq!(hid_usage_id, u16::from(&u));
assert_eq!(hid_usage_id, u.usage_id_value());
// Usage to UsagePage
assert!(matches!(UsagePage::from(&u), UsagePage::GenericDesktop));
// UsagePage to u16
let up = UsagePage::from(&u);
assert_eq!(hid_usage_page, u16::from(&up));
// UsagePage to u16 via AsUsagePage trait
assert_eq!(hid_usage_page, up.usage_page_value());
}
#[test]
fn buttons() {
let hid_usage_page: u16 = 0x9;
let hid_usage_id: u16 = 0x5;
let hid_usage: u32 = ((hid_usage_page as u32) << 16) | hid_usage_id as u32;
let u = Button::Button(5);
assert!(matches!(
Usage::try_from(hid_usage).unwrap(),
Usage::Button(_)
));
// Usage to u32
assert_eq!(u32::from(&u), hid_usage);
assert_eq!(u.usage_value(), hid_usage);
// Usage to u16 usage_id
assert_eq!(hid_usage_id, u16::from(&u));
assert_eq!(hid_usage_id, u.usage_id_value());
// Usage to UsagePage
assert!(matches!(UsagePage::from(&u), UsagePage::Button));
// UsagePage to u16
let up = UsagePage::from(&u);
assert_eq!(hid_usage_page, u16::from(&up));
// UsagePage to u16 via AsUsagePage trait
assert_eq!(hid_usage_page, up.usage_page_value());
}
#[test]
fn ordinals() {
let hid_usage_page: u16 = 0xA;
let hid_usage_id: u16 = 0x8;
let hid_usage: u32 = ((hid_usage_page as u32) << 16) | hid_usage_id as u32;
let u = Ordinal::Ordinal(8);
assert!(matches!(
Usage::try_from(hid_usage).unwrap(),
Usage::Ordinal(_)
));
// Usage to u32
assert_eq!(u32::from(&u), hid_usage);
assert_eq!(u.usage_value(), hid_usage);
// Usage to u16 usage_id
assert_eq!(hid_usage_id, u16::from(&u));
assert_eq!(hid_usage_id, u.usage_id_value());
// Usage to UsagePage
assert!(matches!(UsagePage::from(&u), UsagePage::Ordinal));
// UsagePage to u16
let up = UsagePage::from(&u);
assert_eq!(hid_usage_page, u16::from(&up));
// UsagePage to u16 via AsUsagePage trait
assert_eq!(hid_usage_page, up.usage_page_value());
}
#[cfg(feature = "std")]
#[test]
fn names() {
assert_eq!(UsagePage::GenericDesktop.name().as_str(), "Generic Desktop");
assert_eq!(
UsagePage::PhysicalInputDevice.name().as_str(),
"Physical Input Device"
);
assert_eq!(GenericDesktop::CallMuteLED.name().as_str(), "Call Mute LED");
assert_eq!(VRControls::HeadTracker.name().as_str(), "Head Tracker");
}
#[test]
fn usages() {
let mouse = GenericDesktop::Mouse;
let usage = Usage::GenericDesktop(GenericDesktop::Mouse);
assert_eq!(mouse.usage(), usage);
}
}
{# new line at end of file, even if not shown #}