Skip to main content

hid_types/id/
ident.rs

1//! A utility for handling well-known IDs and reserved values.
2
3/// A container that holds either a known variant, or an integer value with unknown meaning.
4///
5/// # Example
6///
7/// ```rust
8/// #[repr(u8)]
9/// enum KnownColor {
10///     Red = 0,
11///     Green = 1,
12///     Blue = 2,
13/// }
14///
15/// // This can be autogenerated using something like `num_enum::TryFromPrimitive`.
16/// impl TryFrom<u8> for KnownColor {
17///     type Error = Unrecognized;
18///
19///     fn try_from(value: u8) -> Result<Self, Self::Error> {
20///         match value {
21///             0 => Ok(KnownColor::Red),
22///             1 => Ok(KnownColor::Green),
23///             2 => Ok(KnownColor::Blue),
24///             _ => Err(Unrecognized),
25///         }
26///     }
27/// }
28/// struct Unrecognized;
29///
30/// # use hid_types::Ident;
31/// type Color = Ident<KnownColor, u8>;
32///
33/// let green = Color::from_integer(1);
34/// assert!(matches!(green, Color::Known(KnownColor::Green)));
35///
36/// let orange = Color::from_integer(5);
37/// assert!(matches!(orange, Color::Reserved(5)));
38/// ```
39///
40#[derive(Clone, Copy, PartialEq)]
41pub enum Ident<T, Repr> {
42    /// A well-known value.
43    Known(T),
44    /// A reserved value.
45    ///
46    /// This can be used to identify vendor-specific types.
47    Reserved(Repr),
48}
49
50/// Conversion from the "known" type to the `Ident::Known` variant.
51impl<T, Repr> From<T> for Ident<T, Repr> {
52    fn from(value: T) -> Self {
53        Self::Known(value)
54    }
55}
56
57impl<T, Repr> PartialEq<T> for Ident<T, Repr>
58where
59    T: Into<Repr> + Copy,
60    Repr: PartialEq + Copy,
61{
62    fn eq(&self, other: &T) -> bool {
63        self.to_integer() == (*other).into()
64    }
65}
66
67#[cfg(feature = "std")]
68mod std_impls {
69    use super::*;
70
71    use std::fmt::{self, Debug, UpperHex};
72
73    impl<T, Repr> Debug for Ident<T, Repr>
74    where
75        T: Clone + Copy + PartialEq + Debug,
76        Repr: Clone + Copy + PartialEq + Debug + UpperHex,
77    {
78        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79            match self {
80                Self::Known(id) => id.fmt(f),
81                Self::Reserved(id) => f
82                    .debug_tuple("Reserved")
83                    .field(
84                        // FIXME: it would be nice to compute the number of leading zeroes
85                        // for the Repr type.
86                        &format_args!("{:#04X}", id),
87                    )
88                    .finish(),
89            }
90        }
91    }
92}
93
94impl<T, Repr> Ident<T, Repr> {
95    /// Test if the value is reserved.
96    pub fn is_reserved(&self) -> bool {
97        matches!(self, Self::Reserved(_))
98    }
99}
100
101impl<T, Repr> Ident<T, Repr>
102where
103    T: TryFrom<Repr>,
104    Repr: Clone + Copy,
105{
106    /// Try to decode a known identifier; if that fails store the raw number as a "reserved" value.
107    pub fn from_integer(value: Repr) -> Self {
108        match T::try_from(value) {
109            Ok(id) => Self::Known(id),
110            Err(_) => Self::Reserved(value),
111        }
112    }
113}
114
115impl<T, Repr> Ident<T, Repr>
116where
117    T: Into<Repr>,
118{
119    /// Convert the identifier to an integer.
120    pub fn to_integer(self) -> Repr {
121        match self {
122            Ident::Known(id) => id.into(),
123            Ident::Reserved(id) => id,
124        }
125    }
126}