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
57#[cfg(feature = "std")]
58mod std_impls {
59    use super::*;
60
61    use std::fmt::{self, Debug, UpperHex};
62
63    impl<T, Repr> Debug for Ident<T, Repr>
64    where
65        T: Clone + Copy + PartialEq + Debug,
66        Repr: Clone + Copy + PartialEq + Debug + UpperHex,
67    {
68        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69            match self {
70                Self::Known(id) => id.fmt(f),
71                Self::Reserved(id) => f
72                    .debug_tuple("Reserved")
73                    .field(
74                        // FIXME: it would be nice to compute the number of leading zeroes
75                        // for the Repr type.
76                        &format_args!("{:#04X}", id),
77                    )
78                    .finish(),
79            }
80        }
81    }
82}
83
84impl<T, Repr> Ident<T, Repr> {
85    /// Test if the value is reserved.
86    pub fn is_reserved(&self) -> bool {
87        matches!(self, Self::Reserved(_))
88    }
89}
90
91impl<T, Repr> Ident<T, Repr>
92where
93    T: TryFrom<Repr>,
94    Repr: Clone + Copy,
95{
96    /// Try to decode a known identifier; if that fails store the raw number as a "reserved" value.
97    pub fn from_integer(value: Repr) -> Self {
98        match T::try_from(value) {
99            Ok(id) => Self::Known(id),
100            Err(_) => Self::Reserved(value),
101        }
102    }
103}
104
105impl<T, Repr> Ident<T, Repr>
106where
107    T: Into<Repr>,
108{
109    /// Convert the identifier to an integer.
110    pub fn to_integer(self) -> Repr {
111        match self {
112            Ident::Known(id) => id.into(),
113            Ident::Reserved(id) => id,
114        }
115    }
116}