bloop_server_framework/
nfc_uid.rs1use hex::{FromHex, FromHexError, decode_to_slice};
2use serde::{Deserialize, Deserializer, Serialize};
3use thiserror::Error;
4
5#[derive(Debug, Error)]
6pub enum Error {
7 #[error("Invalid UID length, must be 4, 7 or 10")]
8 InvalidLength,
9}
10
11#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
33pub enum NfcUid {
34 Single([u8; 4]),
35 Double([u8; 7]),
36 Triple([u8; 10]),
37}
38
39impl Default for NfcUid {
40 fn default() -> Self {
41 Self::Single(Default::default())
42 }
43}
44
45impl TryFrom<&[u8]> for NfcUid {
46 type Error = Error;
47
48 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
49 match value.len() {
50 4 => Ok(NfcUid::Single(value.try_into().unwrap())),
51 7 => Ok(NfcUid::Double(value.try_into().unwrap())),
52 10 => Ok(NfcUid::Triple(value.try_into().unwrap())),
53 _ => Err(Error::InvalidLength),
54 }
55 }
56}
57
58impl TryFrom<Vec<u8>> for NfcUid {
59 type Error = Error;
60
61 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
62 Self::try_from(&value[..])
63 }
64}
65
66impl FromHex for NfcUid {
67 type Error = FromHexError;
68
69 fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
70 let hex_bytes = hex.as_ref();
71 let mut decoded = vec![0u8; hex_bytes.len() / 2];
72 decode_to_slice(hex, &mut decoded)?;
73
74 NfcUid::try_from(decoded.as_slice()).map_err(|_| FromHexError::InvalidStringLength)
75 }
76}
77
78impl<'de> Deserialize<'de> for NfcUid {
79 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
80 where
81 D: Deserializer<'de>,
82 {
83 let hex = String::deserialize(deserializer)?;
84 NfcUid::from_hex(&hex).map_err(serde::de::Error::custom)
85 }
86}
87
88impl Serialize for NfcUid {
89 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
90 where
91 S: serde::Serializer,
92 {
93 serializer.serialize_str(&hex::encode(self.as_bytes()))
94 }
95}
96
97impl NfcUid {
98 pub fn as_bytes(&self) -> &[u8] {
99 match self {
100 NfcUid::Single(data) => data,
101 NfcUid::Double(data) => data,
102 NfcUid::Triple(data) => data,
103 }
104 }
105
106 pub fn to_vec(&self) -> Vec<u8> {
107 self.as_bytes().to_vec()
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use hex::FromHex;
115
116 #[test]
117 fn from_valid_bytes() {
118 let bytes4 = [0x01, 0x02, 0x03, 0x04];
119 let bytes7 = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
120 let bytes10 = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A];
121
122 let uid4 = NfcUid::try_from(&bytes4[..]).unwrap();
123 let uid7 = NfcUid::try_from(&bytes7[..]).unwrap();
124 let uid10 = NfcUid::try_from(&bytes10[..]).unwrap();
125
126 assert_eq!(uid4.as_bytes(), &bytes4);
127 assert_eq!(uid7.as_bytes(), &bytes7);
128 assert_eq!(uid10.as_bytes(), &bytes10);
129 }
130
131 #[test]
132 fn from_invalid_bytes_length() {
133 let bytes = [0x01, 0x02];
134 let err = NfcUid::try_from(&bytes[..]).unwrap_err();
135 assert!(matches!(err, Error::InvalidLength));
136
137 let bytes = [0u8; 5];
138 let err = NfcUid::try_from(&bytes[..]).unwrap_err();
139 assert!(matches!(err, Error::InvalidLength));
140 }
141
142 #[test]
143 fn from_valid_hex() {
144 let hex4 = "01020304";
145 let hex7 = "01020304050607";
146 let hex10 = "0102030405060708090a";
147
148 let uid4 = NfcUid::from_hex(hex4).unwrap();
149 let uid7 = NfcUid::from_hex(hex7).unwrap();
150 let uid10 = NfcUid::from_hex(hex10).unwrap();
151
152 assert_eq!(uid4.as_bytes(), &[0x01, 0x02, 0x03, 0x04]);
153 assert_eq!(uid7.as_bytes(), &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]);
154 assert_eq!(
155 uid10.as_bytes(),
156 &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a]
157 );
158 }
159
160 #[test]
161 fn from_invalid_hex_format() {
162 let invalid_hex = "xyz"; assert!(NfcUid::from_hex(invalid_hex).is_err());
164
165 let invalid_length = "010203"; let result = NfcUid::from_hex(invalid_length);
167 assert!(matches!(result, Err(FromHexError::InvalidStringLength)));
168 }
169}