1use core::{
12 convert::{TryFrom, TryInto},
13 fmt::{self},
14 hash::Hash,
15 num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8},
16 str::FromStr,
17};
18
19use rand::Rng;
20
21#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
53#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
54#[cfg_attr(feature = "defmt", derive(defmt::Format))]
55#[repr(transparent)]
56pub struct ID([u8; ID::MAX_SIZE]);
57
58impl ID {
59 pub const MAX_SIZE: usize = u128::BITS as usize / 8;
61
62 #[inline]
64 pub fn size(&self) -> usize {
65 Self::MAX_SIZE - (u128::from_le_bytes(self.0).leading_zeros() as usize / 8)
66 }
67
68 #[inline]
81 pub fn to_le_bytes(&self) -> [u8; Self::MAX_SIZE] {
82 self.0
83 }
84
85 #[inline]
87 pub fn rand() -> Self {
88 use rand::rngs::OsRng;
89 let id: u128 = OsRng.gen_range(1..u128::MAX);
90 Self(id.to_le_bytes())
91 }
92}
93
94#[derive(Debug, Clone, Copy, PartialEq, Eq)]
95#[cfg_attr(feature = "defmt", derive(defmt::Format))]
96pub struct SizeError(pub usize);
97
98impl fmt::Display for SizeError {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 if self.0 == 0 {
101 write!(f, "0 is not a valid ID")
102 } else {
103 write!(
104 f,
105 "Maximum ID size of {} bytes exceeded: {} bytes",
106 ID::MAX_SIZE,
107 self.0
108 )
109 }
110 }
111}
112
113#[cfg(feature = "std")]
114impl std::error::Error for SizeError {}
115
116macro_rules! impl_from_sized_slice_for_id {
117 ($N: expr) => {
118 impl TryFrom<&[u8; $N]> for ID {
119 type Error = SizeError;
120
121 fn try_from(value: &[u8; $N]) -> Result<Self, Self::Error> {
124 let mut id = [0u8; ID::MAX_SIZE];
125 id[..$N].copy_from_slice(value);
126 let id = u128::from_le_bytes(id);
127 match NonZeroU128::new(id) {
128 Some(_) => Ok(Self(id.to_le_bytes())),
129 None => Err(SizeError(0)),
130 }
131 }
132 }
133
134 impl TryFrom<[u8; $N]> for ID {
135 type Error = SizeError;
136
137 fn try_from(id: [u8; $N]) -> Result<Self, Self::Error> {
140 (&id).try_into()
141 }
142 }
143 };
144}
145impl_from_sized_slice_for_id!(1);
146impl_from_sized_slice_for_id!(2);
147impl_from_sized_slice_for_id!(3);
148impl_from_sized_slice_for_id!(4);
149impl_from_sized_slice_for_id!(5);
150impl_from_sized_slice_for_id!(6);
151impl_from_sized_slice_for_id!(7);
152impl_from_sized_slice_for_id!(8);
153impl_from_sized_slice_for_id!(9);
154impl_from_sized_slice_for_id!(10);
155impl_from_sized_slice_for_id!(11);
156impl_from_sized_slice_for_id!(12);
157impl_from_sized_slice_for_id!(13);
158impl_from_sized_slice_for_id!(14);
159impl_from_sized_slice_for_id!(15);
160impl_from_sized_slice_for_id!(16);
161
162impl TryFrom<&[u8]> for ID {
163 type Error = SizeError;
164
165 fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
168 let size = slice.len();
169 if size > Self::MAX_SIZE {
170 return Err(SizeError(size));
171 }
172 let mut id = [0u8; ID::MAX_SIZE];
173 id[..size].copy_from_slice(slice);
174 let id = u128::from_le_bytes(id);
175 match NonZeroU128::new(id) {
176 Some(_) => Ok(Self(id.to_le_bytes())),
177 None => Err(SizeError(0)),
178 }
179 }
180}
181
182impl TryFrom<u8> for ID {
183 type Error = SizeError;
184
185 fn try_from(id: u8) -> Result<Self, Self::Error> {
186 id.to_le_bytes().try_into()
187 }
188}
189
190impl From<NonZeroU8> for ID {
191 fn from(id: NonZeroU8) -> Self {
192 NonZeroU128::from(id).into()
193 }
194}
195
196impl TryFrom<u16> for ID {
197 type Error = SizeError;
198
199 fn try_from(id: u16) -> Result<Self, Self::Error> {
200 id.to_le_bytes().try_into()
201 }
202}
203
204impl From<NonZeroU16> for ID {
205 fn from(id: NonZeroU16) -> Self {
206 NonZeroU128::from(id).into()
207 }
208}
209
210impl TryFrom<u32> for ID {
211 type Error = SizeError;
212
213 fn try_from(id: u32) -> Result<Self, Self::Error> {
214 id.to_le_bytes().try_into()
215 }
216}
217
218impl From<NonZeroU32> for ID {
219 fn from(id: NonZeroU32) -> Self {
220 NonZeroU128::from(id).into()
221 }
222}
223
224impl TryFrom<u64> for ID {
225 type Error = SizeError;
226
227 fn try_from(id: u64) -> Result<Self, Self::Error> {
228 id.to_le_bytes().try_into()
229 }
230}
231
232impl From<NonZeroU64> for ID {
233 fn from(id: NonZeroU64) -> Self {
234 NonZeroU128::from(id).into()
235 }
236}
237
238impl TryFrom<u128> for ID {
239 type Error = SizeError;
240
241 fn try_from(id: u128) -> Result<Self, Self::Error> {
242 id.to_le_bytes().try_into()
243 }
244}
245
246impl From<NonZeroU128> for ID {
247 fn from(id: NonZeroU128) -> Self {
248 Self(id.get().to_le_bytes())
249 }
250}
251
252impl FromStr for ID {
253 type Err = ParseIDError;
254
255 fn from_str(s: &str) -> Result<Self, Self::Err> {
256 if s.is_empty() {
257 return Err(ParseIDError::EmptyStringsNotValid);
258 }
259
260 if s.starts_with('0') {
261 return Err(ParseIDError::LeadingZeroNotValid);
262 }
263
264 let bs = u128::from_str_radix(s, 16).map_err(|_| ParseIDError::ParseIntError)?;
265 ID::try_from(bs).map_err(ParseIDError::SizeError)
266 }
267}
268
269#[derive(Debug, Clone, PartialEq, Eq)]
270#[cfg_attr(feature = "defmt", derive(defmt::Format))]
271pub enum ParseIDError {
272 EmptyStringsNotValid,
273 LeadingZeroNotValid,
274 ParseIntError,
275 SizeError(SizeError),
276}
277
278impl fmt::Display for ParseIDError {
279 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280 match self {
281 ParseIDError::EmptyStringsNotValid => write!(f, "Invalid ID (empty string)"),
282 ParseIDError::LeadingZeroNotValid => write!(f, "Invalid ID (leading with '0')"),
283 ParseIDError::ParseIntError => write!(f, "Invalid ID (not an unsigned integer)"),
284 ParseIDError::SizeError(e) => write!(f, "Invalid ID ({e})"),
285 }
286 }
287}
288
289#[cfg(feature = "std")]
290impl std::error::Error for ParseIDError {}
291
292impl fmt::Debug for ID {
293 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
294 write!(f, "{:x}", u128::from_le_bytes(self.0))
295 }
296}
297
298impl fmt::Display for ID {
299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300 fmt::Debug::fmt(self, f)
301 }
302}
303
304mod tests {
305 #[cfg(feature = "std")]
306 #[test]
307 fn parse_display() {
308 let id = "1".parse::<crate::ID>().unwrap();
309
310 assert_eq!(id.to_string(), "1");
311
312 let id = "1bc0".parse::<crate::ID>().unwrap();
313 assert_eq!(id.to_string(), "1bc0");
314
315 let id = "ff00".parse::<crate::ID>().unwrap();
316 assert_eq!(id.to_string(), "ff00");
317
318 let id = "ff0".parse::<crate::ID>().unwrap();
319 assert_eq!(id.to_string(), "ff0");
320
321 let id = "abcd".parse::<crate::ID>().unwrap();
322 assert_eq!(id.to_string(), "abcd");
323
324 let id = "6bd9cb5f9f2644508fbbb0df1d6cce3a"
325 .parse::<crate::ID>()
326 .unwrap();
327 assert_eq!(id.to_string(), "6bd9cb5f9f2644508fbbb0df1d6cce3a");
328
329 "0".parse::<crate::ID>().unwrap_err();
330 "0bcd".parse::<crate::ID>().unwrap_err();
331 "6bd9cb5f9f2644508fbbb0df1d6cce3a0"
332 .parse::<crate::ID>()
333 .unwrap_err();
334 }
335}