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}