guid_create/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2//! # guid-create
3//!
4//! Rust helper for randomly creating GUIDs.
5//!
6//! ```rust,ignore
7//! extern crate guid_create;
8//! use guid_create::GUID;
9//!
10//! // Create GUIDs
11//! let guid = GUID::rand();
12//! let guid = GUID::parse("87935CDE-7094-4C2B-A0F4-DD7D512DD261").unwrap();
13//! let guid = GUID::build_from_components(0x87935CDE, 0x7094, 0x4C2B, &[0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61], );
14//! let guid = GUID::build_from_slice(&[ 0x87, 0x93, 0x5C, 0xDE, 0x70, 0x94, 0x4C, 0x2B, 0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61,]);
15//!
16//! // View GUIDs
17//! guid.to_string(); // 87935CDE-7094-4C2B-A0F4-DD7D512DD261
18//!
19//! // Check GUIDs
20//! guid.data1();
21//! guid.data2();
22//! guid.data3();
23//! guid.data4();
24//! ```
25#[cfg(test)]
26extern crate quickcheck;
27#[cfg(test)]
28#[macro_use(quickcheck)]
29extern crate quickcheck_macros;
30
31use core::{convert::TryInto, fmt};
32
33#[cfg(windows)]
34use winapi::shared::guiddef::GUID as WinGuid;
35
36/// Parsing error type.
37#[derive(Debug)]
38pub struct ParseError;
39
40impl fmt::Display for ParseError {
41 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
42 write!(
43 f,
44 "Malformed GUID, expecting XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
45 )
46 }
47}
48impl core::error::Error for ParseError {}
49
50/// A GUID backed by 16 byte array.
51#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, Hash)]
52#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
53#[repr(transparent)]
54pub struct GUID {
55 data: [u8; 16],
56}
57
58impl From<CGuid> for GUID {
59 fn from(item: CGuid) -> Self {
60 GUID::build_from_components(item.a, item.b, item.c, &item.d)
61 }
62}
63
64impl From<GUID> for CGuid {
65 fn from(item: GUID) -> Self {
66 CGuid {
67 a: item.data1(),
68 b: item.data2(),
69 c: item.data3(),
70 d: item.data4(),
71 }
72 }
73}
74
75impl fmt::Display for CGuid {
76 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77 let guid: GUID = (*self).into();
78 guid.fmt(f)
79 }
80}
81
82#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, Hash)]
83#[repr(C)]
84pub struct CGuid {
85 /// The low field of the timestamp.
86 a: u32,
87 /// The middle field of the timestamp.
88 b: u16,
89 /// The high field of the timestamp multiplexed with the version number.
90 c: u16,
91 /// Contains, in this order:
92 /// - The high field of the clock sequence multiplexed with the variant.
93 /// - The low field of the clock sequence.
94 /// - The spatially unique node identifier.
95 d: [u8; 8],
96}
97
98impl GUID {
99 /// Construct a `GUID` from components.
100 ///
101 /// ``` rust
102 /// let guid = guid_create::GUID::build_from_components(
103 /// 0x87935CDE,
104 /// 0x7094,
105 /// 0x4C2B,
106 /// &[ 0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61 ]
107 /// );
108 ///
109 /// assert_eq!(guid.data1(), 0x87935CDE);
110 /// assert_eq!(guid.data2(), 0x7094);
111 /// assert_eq!(guid.data3(), 0x4C2B);
112 /// assert_eq!(guid.data4(), [ 0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61 ]);
113 /// assert_eq!(guid.to_string(), "87935CDE-7094-4C2B-A0F4-DD7D512DD261");
114 /// ```
115 pub const fn build_from_components(d1: u32, d2: u16, d3: u16, d4: &[u8; 8]) -> Self {
116 let d1 = d1.to_be_bytes();
117 let d2 = d2.to_be_bytes();
118 let d3 = d3.to_be_bytes();
119 #[rustfmt::skip]
120 let data = [
121 d1[0], d1[1], d1[2], d1[3],
122 d2[0], d2[1],
123 d3[0], d3[1],
124 d4[0], d4[1], d4[2], d4[3], d4[4], d4[5], d4[6], d4[7],
125 ];
126
127 GUID { data }
128 }
129
130 /// Construct a `GUID` from 16 bytes.
131 ///
132 /// ``` rust
133 /// let guid = guid_create::GUID::build_from_slice(&[
134 /// 0x87, 0x93, 0x5C, 0xDE, 0x70, 0x94, 0x4C, 0x2B, 0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D,
135 /// 0xD2, 0x61,
136 /// ]);
137 ///
138 /// assert_eq!(guid.data1(), 0x87935CDE);
139 /// assert_eq!(guid.data2(), 0x7094);
140 /// assert_eq!(guid.data3(), 0x4C2B);
141 /// assert_eq!(
142 /// guid.data4(),
143 /// [0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61]
144 /// );
145 /// assert_eq!(guid.to_string(), "87935CDE-7094-4C2B-A0F4-DD7D512DD261");
146 /// ```
147 pub const fn build_from_slice(data: &[u8; 16]) -> Self {
148 GUID { data: *data }
149 }
150
151 /// Construct a `GUID` from a string.
152 ///
153 /// ``` rust
154 /// let guid = guid_create::GUID::parse("87935CDE-7094-4C2B-A0F4-DD7D512DD261").unwrap();
155 ///
156 /// assert_eq!(guid.data1(), 0x87935CDE);
157 /// assert_eq!(guid.data2(), 0x7094);
158 /// assert_eq!(guid.data3(), 0x4C2B);
159 /// assert_eq!(guid.data4(), [ 0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61 ]);
160 /// assert_eq!(guid.to_string(), "87935CDE-7094-4C2B-A0F4-DD7D512DD261");
161 /// ```
162 pub fn parse(guid: &str) -> Result<Self, ParseError> {
163 fn n(ch: u8) -> Result<u8, ParseError> {
164 match ch {
165 b'0'..=b'9' => Ok(ch - 48),
166 b'A'..=b'F' => Ok(ch - 55),
167 b'a'..=b'f' => Ok(ch - 87),
168 _ => Err(ParseError),
169 }
170 }
171 fn hexbyte(s: &[u8]) -> Result<(u8, &[u8]), ParseError> {
172 match s {
173 [a, b, tail @ ..] => n(*a)
174 .and_then(|a| n(*b).map(|b| a * 16 + b))
175 .map(|x| (x, tail)),
176 _ => Err(ParseError),
177 }
178 }
179 fn strip_dash(s: &[u8]) -> Result<&[u8], ParseError> {
180 match s {
181 [b'-', tail @ ..] => Ok(tail),
182 _ => Err(ParseError),
183 }
184 }
185
186 let mut data = [0u8; 16];
187
188 let mut s = guid.as_bytes();
189
190 fn fill<'a>(buf: &mut [u8], mut s: &'a [u8]) -> Result<&'a [u8], ParseError> {
191 for l in buf {
192 let (d, s_) = hexbyte(s)?;
193 *l = d;
194 s = s_;
195 }
196 Ok(s)
197 }
198
199 // first four bytes
200 s = fill(&mut data[..4], s)?;
201 s = strip_dash(s)?;
202
203 // second two bytes
204 s = fill(&mut data[4..6], s)?;
205 s = strip_dash(s)?;
206
207 // third two bytes
208 s = fill(&mut data[6..8], s)?;
209 s = strip_dash(s)?;
210
211 // fourth two bytes
212 s = fill(&mut data[8..10], s)?;
213 s = strip_dash(s)?;
214
215 // trailing bytes
216 s = fill(&mut data[10..], s)?;
217
218 // should be empty!
219 if s.is_empty() {
220 Ok(Self { data })
221 } else {
222 Err(ParseError)
223 }
224 }
225
226 /// Generates a new GUID with 16 random bytes.
227 #[cfg(feature = "rand")]
228 pub fn rand() -> GUID {
229 GUID {
230 data: rand::random(),
231 }
232 }
233
234 /// The first four bytes.
235 ///
236 /// ``` rust
237 /// extern crate guid_create;
238 /// let guid = guid_create::GUID::build_from_components(
239 /// 500,
240 /// 600,
241 /// 700,
242 /// &[ 0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61 ]
243 /// );
244 ///
245 /// assert_eq!(guid.data1(), 500);
246 /// ```
247 pub fn data1(&self) -> u32 {
248 u32::from_be_bytes(
249 self.data[0..4]
250 .try_into()
251 .expect("slice with incorrect length"),
252 )
253 }
254
255 /// Bytes 5 and 6.
256 ///
257 /// ``` rust
258 /// extern crate guid_create;
259 /// let guid = guid_create::GUID::build_from_components(
260 /// 500,
261 /// 600,
262 /// 700,
263 /// &[ 0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61 ]
264 /// );
265 ///
266 /// assert_eq!(guid.data2(), 600);
267 /// ```
268 pub fn data2(&self) -> u16 {
269 u16::from_be_bytes(
270 self.data[4..6]
271 .try_into()
272 .expect("slice with incorrect length"),
273 )
274 }
275
276 /// Bytes 7 and 8.
277 ///
278 /// ``` rust
279 /// extern crate guid_create;
280 /// let guid = guid_create::GUID::build_from_components(
281 /// 500,
282 /// 600,
283 /// 700,
284 /// &[ 0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61 ]
285 /// );
286 ///
287 /// assert_eq!(guid.data3(), 700);
288 /// ```
289 pub fn data3(&self) -> u16 {
290 u16::from_be_bytes(
291 self.data[6..8]
292 .try_into()
293 .expect("slice with incorrect length"),
294 )
295 }
296
297 /// The last eight bytes.
298 ///
299 /// ``` rust
300 /// extern crate guid_create;
301 /// let guid = guid_create::GUID::build_from_components(
302 /// 500,
303 /// 600,
304 /// 700,
305 /// &[ 0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61 ]
306 /// );
307 ///
308 /// assert_eq!(
309 /// guid.data4(),
310 /// [0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61]
311 /// );
312 /// ```
313 pub fn data4(&self) -> [u8; 8] {
314 self.data[8..16]
315 .try_into()
316 .expect("slice with incorrect length")
317 }
318
319 /// Convert the `GUID` to a `winapi` [GUID](https://docs.rs/winapi/0.3.4/x86_64-pc-windows-msvc/winapi/shared/guiddef/struct.GUID.html)
320 /// > Only present on windows targets
321 ///
322 /// ``` rust
323 /// extern crate guid_create;
324 /// let guid = guid_create::GUID::build_from_components(
325 /// 0x87935CDE,
326 /// 0x7094,
327 /// 0x4C2B,
328 /// &[ 0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61 ]
329 /// );
330 ///
331 /// let win = guid.as_winapi_guid();
332 /// assert_eq!(win.Data1, 0x87935CDE);
333 /// assert_eq!(win.Data2, 0x7094);
334 /// assert_eq!(win.Data3, 0x4C2B);
335 /// assert_eq!(win.Data4, [ 0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61 ]);
336 /// ```
337 #[cfg(windows)]
338 pub fn as_winapi_guid(&self) -> WinGuid {
339 WinGuid {
340 Data1: self.data1(),
341 Data2: self.data2(),
342 Data3: self.data3(),
343 Data4: self.data4(),
344 }
345 }
346
347 /// Convert a `winapi` [GUID](https://docs.rs/winapi/0.3.4/x86_64-pc-windows-msvc/winapi/shared/guiddef/struct.GUID.html) to a `GUID`
348 /// > Only present on windows targets
349 ///
350 /// ``` rust
351 /// extern crate guid_create;
352 /// extern crate winapi;
353 /// let win = winapi::shared::guiddef::GUID {
354 /// Data1: 0x87935CDE,
355 /// Data2: 0x7094,
356 /// Data3: 0x4C2B,
357 /// Data4: [ 0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61 ]
358 /// };
359 ///
360 /// let guid = guid_create::GUID::from_winapi_guid(win);
361 /// assert_eq!(guid.data1(), 0x87935CDE);
362 /// assert_eq!(guid.data2(), 0x7094);
363 /// assert_eq!(guid.data3(), 0x4C2B);
364 /// assert_eq!(guid.data4(), [ 0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61 ]);
365 /// ```
366 #[cfg(windows)]
367 pub fn from_winapi_guid(guid: WinGuid) -> Self {
368 GUID::build_from_components(guid.Data1, guid.Data2, guid.Data3, &guid.Data4)
369 }
370}
371
372#[cfg(feature = "serde")]
373use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
374
375#[cfg(feature = "serde")]
376impl<'de> Deserialize<'de> for GUID {
377 fn deserialize<D>(deserializer: D) -> Result<GUID, D::Error>
378 where
379 D: Deserializer<'de>,
380 {
381 let string_guid = String::deserialize(deserializer)?;
382 let guid = GUID::parse(&string_guid)
383 .map_err(|_| de::Error::custom(format!("cannot convert {string_guid} to guid")))?;
384 Ok(guid)
385 }
386}
387
388#[cfg(feature = "serde")]
389impl Serialize for GUID {
390 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
391 serializer.serialize_str(&*self.to_string())
392 }
393}
394
395impl fmt::Display for GUID {
396 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
397 write!(
398 f,
399 "{:08X}-{:04X}-{:04X}-{:04X}-{:08X}{:04X}",
400 self.data1(),
401 self.data2(),
402 self.data3(),
403 u16::from_be_bytes([self.data[8], self.data[9]]),
404 u32::from_be_bytes([self.data[10], self.data[11], self.data[12], self.data[13]]),
405 u16::from_be_bytes([self.data[14], self.data[15]]),
406 )
407 }
408}
409
410#[cfg(test)]
411impl quickcheck::Arbitrary for GUID {
412 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
413 let mut data = [0u8; 16];
414 data.fill_with(|| u8::arbitrary(g));
415 Self { data }
416 }
417}
418
419#[cfg(test)]
420mod tests {
421 use super::*;
422
423 #[test]
424 fn travis_test() {}
425
426 #[test]
427 #[cfg(feature = "std")]
428 fn string_lengths() {
429 for _ in 0..10000 {
430 let guid = GUID::rand();
431 let s = guid.to_string();
432 println!("{}", s);
433 assert_eq!(s.len(), 36);
434 }
435 }
436
437 #[cfg(windows)]
438 #[test]
439 fn win_guid() {
440 for _ in 0..10000 {
441 let guid = GUID::rand();
442 let win = guid.as_winapi_guid();
443 assert_eq!(guid.data1(), win.Data1);
444 assert_eq!(guid.data2(), win.Data2);
445 assert_eq!(guid.data3(), win.Data3);
446 assert_eq!(guid.data4(), win.Data4);
447 let convert_back = GUID::from_winapi_guid(win);
448 assert_eq!(guid, convert_back);
449 }
450 }
451
452 #[test]
453 #[cfg(feature = "std")]
454 fn create_from_components() {
455 let guid = GUID::build_from_components(
456 0x87935CDE,
457 0x7094,
458 0x4C2B,
459 &[0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61],
460 );
461
462 assert_eq!(guid.data1(), 0x87935CDE);
463 assert_eq!(guid.data2(), 0x7094);
464 assert_eq!(guid.data3(), 0x4C2B);
465 assert_eq!(
466 guid.data4(),
467 [0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61]
468 );
469 assert_eq!(guid.to_string(), "87935CDE-7094-4C2B-A0F4-DD7D512DD261");
470 }
471
472 #[test]
473 #[cfg(feature = "std")]
474 fn create_from_array() {
475 let guid = GUID::build_from_slice(&[
476 0x87, 0x93, 0x5C, 0xDE, 0x70, 0x94, 0x4C, 0x2B, 0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D,
477 0xD2, 0x61,
478 ]);
479
480 println!("{}", guid);
481
482 assert_eq!(guid.data1(), 0x87935CDE);
483 assert_eq!(guid.data2(), 0x7094);
484 assert_eq!(guid.data3(), 0x4C2B);
485 assert_eq!(
486 guid.data4(),
487 [0xA0, 0xF4, 0xDD, 0x7D, 0x51, 0x2D, 0xD2, 0x61]
488 );
489 assert_eq!(guid.to_string(), "87935CDE-7094-4C2B-A0F4-DD7D512DD261");
490 }
491
492 #[test]
493 #[cfg(feature = "std")]
494 fn parse_strings() {
495 for _ in 0..10000 {
496 let guid = GUID::rand();
497 let s = guid.to_string();
498 let guid2 = GUID::parse(&s).unwrap();
499 assert_eq!(guid, guid2);
500 }
501 }
502
503 #[quickcheck]
504 #[cfg(feature = "std")]
505 fn no_panic_parse(s: String) {
506 GUID::parse(&s).ok();
507 }
508
509 #[quickcheck]
510 #[cfg(feature = "std")]
511 fn parse_success(guid: GUID) -> bool {
512 let s = guid.to_string();
513 let g2 = GUID::parse(&s).unwrap();
514 g2 == guid
515 }
516}