Skip to main content

irox_tools/util/
uuid.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2023 IROX Contributors
3//
4
5//!
6//! A basic implementation of a UUID
7//!
8
9use crate::cfg_feature_serde;
10use core::fmt::{Display, Formatter};
11use irox_bits::{Bits, Error, MutBits};
12
13///
14/// A basic UUID structure.
15#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
16pub struct UUID {
17    inner: u128,
18}
19
20impl From<u128> for UUID {
21    fn from(value: u128) -> Self {
22        UUID { inner: value }
23    }
24}
25
26impl From<&u128> for UUID {
27    fn from(value: &u128) -> Self {
28        UUID { inner: *value }
29    }
30}
31
32impl From<UUID> for u128 {
33    fn from(value: UUID) -> Self {
34        value.inner
35    }
36}
37
38impl From<&UUID> for u128 {
39    fn from(value: &UUID) -> Self {
40        value.inner
41    }
42}
43impl From<UUID> for [u8; 16] {
44    fn from(value: UUID) -> Self {
45        value.inner.to_be_bytes()
46    }
47}
48
49impl From<&UUID> for [u8; 16] {
50    fn from(value: &UUID) -> Self {
51        value.inner.to_be_bytes()
52    }
53}
54
55impl From<[u8; 16]> for UUID {
56    fn from(value: [u8; 16]) -> Self {
57        u128::from_be_bytes(value).into()
58    }
59}
60
61#[derive(Debug, Copy, Clone, Eq, PartialEq)]
62pub enum UUIDParseError {
63    WrongSize,
64    InvalidCharacter,
65}
66impl Display for UUIDParseError {
67    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
68        match self {
69            UUIDParseError::WrongSize => write!(f, "Wrong Size"),
70            UUIDParseError::InvalidCharacter => write!(f, "Invalid Character"),
71        }
72    }
73}
74
75impl TryFrom<&[u8]> for UUID {
76    type Error = UUIDParseError;
77
78    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
79        if value.len() != 16 {
80            return Err(UUIDParseError::WrongSize);
81        }
82        let mut inner = 0u128;
83        let mut shift = 128;
84        for b in value {
85            shift -= 8;
86            let b = (*b as u128).wrapping_shl(shift);
87            inner |= b;
88        }
89        Ok(UUID { inner })
90    }
91}
92
93impl TryFrom<&str> for UUID {
94    type Error = UUIDParseError;
95
96    fn try_from(value: &str) -> Result<Self, Self::Error> {
97        let no_dashes = value.replace('-', "");
98        if no_dashes.len() != 32 {
99            return Err(UUIDParseError::WrongSize);
100        }
101        let mut inner: u128 = 0;
102        let mut shift = 128;
103        for c in no_dashes.as_bytes() {
104            let val = match *c as char {
105                '0' => 0u8,
106                '1' => 1,
107                '2' => 2,
108                '3' => 3,
109                '4' => 4,
110                '5' => 5,
111                '6' => 6,
112                '7' => 7,
113                '8' => 8,
114                '9' => 9,
115                'A' | 'a' => 10,
116                'B' | 'b' => 11,
117                'C' | 'c' => 12,
118                'D' | 'd' => 13,
119                'E' | 'e' => 14,
120                'F' | 'f' => 15,
121                _ => return Err(UUIDParseError::InvalidCharacter),
122            };
123            shift -= 4;
124            inner |= (val as u128).wrapping_shl(shift);
125        }
126        Ok(UUID { inner })
127    }
128}
129cfg_feature_serde! {
130    struct UUIDVisitor;
131    impl serde::de::Visitor<'_> for UUIDVisitor {
132        type Value = UUID;
133
134        fn expecting(&self, fmt: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
135            write!(fmt, "The visitor expects to receive a string formatted as a UUID")
136        }
137        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: serde::de::Error {
138            UUID::try_from(v).map_err(serde::de::Error::custom)
139        }
140    }
141    impl<'de> serde::Deserialize<'de> for UUID {
142        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
143            deserializer.deserialize_str(UUIDVisitor)
144        }
145    }
146    crate::cfg_feature_alloc!{
147        use alloc::string::ToString;
148        impl serde::Serialize for UUID {
149            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
150                serializer.serialize_str(&self.to_string())
151            }
152        }
153    }
154}
155///
156/// A trait that can be applied to a Reader, or other bit stream.
157pub trait UUIDReader {
158    ///
159    /// Attempts to read a UUID from this data source, returning the UUID read, or an error if it
160    /// could not.
161    fn read_uuid(&mut self) -> Result<UUID, Error>;
162}
163
164impl<T> UUIDReader for T
165where
166    T: Bits,
167{
168    fn read_uuid(&mut self) -> Result<UUID, Error> {
169        Ok(self.read_be_u128()?.into())
170    }
171}
172
173///
174/// A trait that can be applied to a Writer, or other bit stream.
175pub trait UUIDWriter {
176    ///
177    /// Attempts to write a UUID to this data source
178    fn write_uuid(&mut self, uuid: &UUID) -> Result<(), Error>;
179}
180
181impl<T> UUIDWriter for T
182where
183    T: MutBits,
184{
185    fn write_uuid(&mut self, uuid: &UUID) -> Result<(), Error> {
186        self.write_be_u128(uuid.inner)
187    }
188}
189
190impl Display for UUID {
191    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
192        // 8-4-4-4-12 chars : 4-2-2-2-6 bytes : 32-16-16-16-48 bits
193        let a = (self.inner & 0xFFFFFFFF_0000_0000_0000_000000000000) >> 96;
194        let b = (self.inner & 0x00000000_FFFF_0000_0000_000000000000) >> 80;
195        let c = (self.inner & 0x00000000_0000_FFFF_0000_000000000000) >> 64;
196        let d = (self.inner & 0x00000000_0000_0000_FFFF_000000000000) >> 48;
197        let e = self.inner & 0x00000000_0000_0000_0000_FFFFFFFFFFFF;
198        f.write_fmt(format_args!("{a:08X}-{b:04X}-{c:04X}-{d:04X}-{e:012X}"))
199    }
200}
201
202impl UUID {
203    ///
204    /// Generates a new random UUID
205    #[must_use]
206    pub fn new_random() -> UUID {
207        use crate::random::PRNG;
208        let mut random = crate::random::Random::default();
209        UUID {
210            inner: random.next_u128(),
211        }
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    use crate::uuid::{UUIDParseError, UUID};
218
219    #[test]
220    pub fn display_test() {
221        let uuid = UUID { inner: 0 };
222        assert_eq!("00000000-0000-0000-0000-000000000000", format!("{uuid}"));
223
224        let uuid = UUID { inner: u128::MAX };
225        assert_eq!("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF", format!("{uuid}"));
226    }
227
228    #[test]
229    pub fn parse_test() -> Result<(), UUIDParseError> {
230        let uuid = UUID::new_random();
231        let disp = format!("{uuid}");
232
233        let parsed: UUID = disp.as_str().try_into()?;
234        assert_eq!(parsed, uuid);
235
236        let parsed: u128 = parsed.into();
237        let parsed: UUID = parsed.to_be_bytes().as_slice().try_into()?;
238        assert_eq!(parsed, uuid);
239
240        Ok(())
241    }
242
243    #[test]
244    #[cfg(all(feature = "serde", feature = "std"))]
245    pub fn serde_test() -> Result<(), UUIDParseError> {
246        #[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Debug)]
247        struct Test {
248            a: UUID,
249        }
250        impl Default for Test {
251            fn default() -> Self {
252                Self { a: 0u128.into() }
253            }
254        }
255        let a = Test { a: 128u128.into() };
256        let s = serde_json::to_string(&a).unwrap_or_default();
257        assert_eq!(s, "{\"a\":\"00000000-0000-0000-0000-000000000080\"}");
258        let b: Test = serde_json::from_str(&s).unwrap();
259        assert_eq!(a, b);
260        Ok(())
261    }
262}