1use std::fmt::{self, Display, Formatter};
2use std::ops::Deref;
3use std::str::FromStr;
4
5const BYTE_MAP: [usize; 16] = [3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15];
8
9#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug)]
12#[repr(transparent)]
13pub struct Guid([u8; 16]);
14
15impl Deref for Guid {
16 type Target = [u8; 16];
17
18 fn deref(&self) -> &Self::Target {
19 &self.0
20 }
21}
22
23impl Display for Guid {
24 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
25 write!(
26 f,
27 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
28 self.0[BYTE_MAP[0]],
29 self.0[BYTE_MAP[1]],
30 self.0[BYTE_MAP[2]],
31 self.0[BYTE_MAP[3]],
32 self.0[BYTE_MAP[4]],
33 self.0[BYTE_MAP[5]],
34 self.0[BYTE_MAP[6]],
35 self.0[BYTE_MAP[7]],
36 self.0[BYTE_MAP[8]],
37 self.0[BYTE_MAP[9]],
38 self.0[BYTE_MAP[10]],
39 self.0[BYTE_MAP[11]],
40 self.0[BYTE_MAP[12]],
41 self.0[BYTE_MAP[13]],
42 self.0[BYTE_MAP[14]],
43 self.0[BYTE_MAP[15]],
44 )
45 }
46}
47
48impl FromStr for Guid {
49 type Err = &'static str;
50
51 fn from_str(s: &str) -> Result<Self, Self::Err> {
52 match s.len() {
53 36 => Self::from_str_with_hyphens(s),
54 32 => Self::from_str_without_hyphens(s),
55 _ => Err("Invalid length, not a GUID"),
56 }
57 }
58}
59
60const GUID_PARSE_ERR: &str = "Failed to parse GUID bytes";
61
62impl Guid {
63 pub const fn from_ms_bytes(raw: &[u8; 16]) -> Self {
67 Self([
68 raw[0], raw[1], raw[2], raw[3], raw[4], raw[5], raw[6], raw[7], raw[8], raw[9],
69 raw[10], raw[11], raw[12], raw[13], raw[14], raw[15],
70 ])
71 }
72
73 pub const fn to_ms_bytes(self) -> [u8; 16] {
76 self.0
77 }
78
79 fn from_str_without_hyphens(s: &str) -> Result<Self, &'static str> {
80 let mut buf = [0u8; 16];
81 for i in 0..16 {
84 if let Ok(v) = u8::from_str_radix(&s[i * 2..i * 2 + 2], 16) {
85 buf[BYTE_MAP[i]] = v
86 } else {
87 return Err(GUID_PARSE_ERR);
88 }
89 }
90 Ok(Guid(buf))
91 }
92
93 #[inline]
94 fn from_str_with_hyphens(s: &str) -> Result<Self, &'static str> {
95 let without_hyphens: String = s.split('-').collect();
97 Self::from_str_without_hyphens(&without_hyphens)
98 }
99
100 pub const fn from_le_bytes(b: [u8; 16]) -> Self {
101 Self([
102 b[15 - BYTE_MAP[0]],
103 b[15 - BYTE_MAP[1]],
104 b[15 - BYTE_MAP[2]],
105 b[15 - BYTE_MAP[3]],
106 b[15 - BYTE_MAP[4]],
107 b[15 - BYTE_MAP[5]],
108 b[15 - BYTE_MAP[6]],
109 b[15 - BYTE_MAP[7]],
110 b[15 - BYTE_MAP[8]],
111 b[15 - BYTE_MAP[9]],
112 b[15 - BYTE_MAP[10]],
113 b[15 - BYTE_MAP[11]],
114 b[15 - BYTE_MAP[12]],
115 b[15 - BYTE_MAP[13]],
116 b[15 - BYTE_MAP[14]],
117 b[15 - BYTE_MAP[15]],
118 ])
119 }
120
121 pub const fn to_le_bytes(self) -> [u8; 16] {
123 [
124 self.0[BYTE_MAP[15]],
125 self.0[BYTE_MAP[14]],
126 self.0[BYTE_MAP[13]],
127 self.0[BYTE_MAP[12]],
128 self.0[BYTE_MAP[11]],
129 self.0[BYTE_MAP[10]],
130 self.0[BYTE_MAP[9]],
131 self.0[BYTE_MAP[8]],
132 self.0[BYTE_MAP[7]],
133 self.0[BYTE_MAP[6]],
134 self.0[BYTE_MAP[5]],
135 self.0[BYTE_MAP[4]],
136 self.0[BYTE_MAP[3]],
137 self.0[BYTE_MAP[2]],
138 self.0[BYTE_MAP[1]],
139 self.0[BYTE_MAP[0]],
140 ]
141 }
142
143 pub const fn from_be_bytes(b: [u8; 16]) -> Self {
144 Self([
145 b[BYTE_MAP[0]],
146 b[BYTE_MAP[1]],
147 b[BYTE_MAP[2]],
148 b[BYTE_MAP[3]],
149 b[BYTE_MAP[4]],
150 b[BYTE_MAP[5]],
151 b[BYTE_MAP[6]],
152 b[BYTE_MAP[7]],
153 b[BYTE_MAP[8]],
154 b[BYTE_MAP[9]],
155 b[BYTE_MAP[10]],
156 b[BYTE_MAP[11]],
157 b[BYTE_MAP[12]],
158 b[BYTE_MAP[13]],
159 b[BYTE_MAP[14]],
160 b[BYTE_MAP[15]],
161 ])
162 }
163
164 pub const fn to_be_bytes(self) -> [u8; 16] {
166 [
167 self.0[BYTE_MAP[0]],
168 self.0[BYTE_MAP[1]],
169 self.0[BYTE_MAP[2]],
170 self.0[BYTE_MAP[3]],
171 self.0[BYTE_MAP[4]],
172 self.0[BYTE_MAP[5]],
173 self.0[BYTE_MAP[6]],
174 self.0[BYTE_MAP[7]],
175 self.0[BYTE_MAP[8]],
176 self.0[BYTE_MAP[9]],
177 self.0[BYTE_MAP[10]],
178 self.0[BYTE_MAP[11]],
179 self.0[BYTE_MAP[12]],
180 self.0[BYTE_MAP[13]],
181 self.0[BYTE_MAP[14]],
182 self.0[BYTE_MAP[15]],
183 ]
184 }
185}
186
187#[cfg(test)]
188const BYTES_BE: [u8; 16] = [
189 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
190];
191#[cfg(test)]
192const BYTES_LE: [u8; 16] = [
193 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
194];
195#[cfg(test)]
196const BYTES_MS: [u8; 16] = [
197 0x03, 0x02, 0x01, 0x00, 0x05, 0x04, 0x07, 0x06, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
198];
199#[cfg(test)]
200const BYTES_STR: &str = "00010203-0405-0607-0809-0a0b0c0d0e0f";
201#[cfg(test)]
202const BYTES_STR_NO_HYPH: &str = "000102030405060708090a0b0c0d0e0f";
203
204#[test]
205fn from_ms_bytes() {
206 assert_eq!(Guid::from_ms_bytes(&BYTES_MS).0, BYTES_MS);
207}
208
209#[test]
210fn to_ms_bytes() {
211 assert_eq!(Guid(BYTES_MS).to_ms_bytes(), BYTES_MS);
212}
213
214#[test]
215fn from_le_bytes() {
216 assert_eq!(Guid::from_le_bytes(BYTES_LE).0, BYTES_MS);
217}
218
219#[test]
220fn to_le_bytes() {
221 assert_eq!(Guid(BYTES_MS).to_le_bytes(), BYTES_LE);
222}
223
224#[test]
225fn from_be_bytes() {
226 assert_eq!(Guid::from_be_bytes(BYTES_BE).0, BYTES_MS);
227}
228
229#[test]
230fn to_be_bytes() {
231 assert_eq!(Guid(BYTES_MS).to_be_bytes(), BYTES_BE);
232}
233
234#[test]
235fn from_str() {
236 assert_eq!(Guid::from_str(BYTES_STR).unwrap().0, BYTES_MS);
237 assert_eq!(Guid::from_str(BYTES_STR_NO_HYPH).unwrap().0, BYTES_MS);
238 assert_eq!(Guid::from_str_with_hyphens(BYTES_STR).unwrap().0, BYTES_MS);
239 assert_eq!(
240 Guid::from_str_without_hyphens(BYTES_STR_NO_HYPH).unwrap().0,
241 BYTES_MS
242 );
243}
244
245#[test]
246fn to_str() {
247 assert_eq!(Guid(BYTES_MS).to_string(), BYTES_STR);
248}