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