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