//! Simple bindings to libuuid's `uuid_generate`. use std::borrow::{Borrow, BorrowMut}; use std::fmt; #[cfg(not(target_arch = "wasm32"))] mod lib_uuid_bindings; #[cfg(not(target_arch = "wasm32"))] use lib_uuid_bindings::*; #[cfg(target_arch = "wasm32")] mod wasm; #[cfg(target_arch = "wasm32")] pub use wasm::*; /// An error parsing a uuid from a string. #[derive(Debug, Clone, Copy)] pub struct UuidError; impl fmt::Display for UuidError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("bad uuid format") } } impl std::error::Error for UuidError {} /// Check for hex digits. fn hex_digit(b: u8) -> Result { Ok(match b { b'0'..=b'9' => b - b'0', b'a'..=b'f' => b - b'a' + 0xA, b'A'..=b'F' => b - b'A' + 0xA, _ => return Err(UuidError), }) } /// Uuid generated with the system's native libuuid. /// /// ``` /// use proxmox_uuid::Uuid; /// /// let uuid = Uuid::generate(); /// println!("Generated uuid: {}", uuid); /// // prints somethign like: /// // Generated uuid: 65b85639-78d7-4330-85c6-39502b2f9b01 /// /// let bytes: &[u8] = uuid.as_ref(); /// println!("raw byte string: {:?}", bytes); /// // raw byte string: [101, 184, 86, 57, 120, 215, 67, 48, 133, 198, 57, 80, 43, 47, 155, 1] /// /// let text = format!("{}", uuid); /// let parsed: Uuid = text.parse().unwrap(); /// assert_eq!(uuid, parsed); /// ``` #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Uuid(Box<[u8; 16]>); impl Uuid { /// Generate a uuid with `uuid_generate(3)`. pub fn generate() -> Self { use std::alloc::{alloc, Layout}; let uuid = unsafe { alloc(Layout::new::<[u8; 16]>()) as *mut [u8; 16] }; unsafe { uuid_generate(uuid) }; Self(unsafe { Box::from_raw(uuid) }) } /// Get a reference to the internal 16 byte array. pub fn as_bytes(&self) -> &[u8; 16] { &self.0 } /// Take out the inner boxed 16 byte array. pub fn into_inner(self) -> Box<[u8; 16]> { self.0 } /// Parse a uuid in optionally-hyphenated format. /// /// ``` /// use proxmox_uuid::Uuid; /// /// let gen = Uuid::generate(); /// let text = format!("{}", gen); /// let parsed: Uuid = text.parse().unwrap(); /// assert_eq!(gen, parsed); /// /// let uuid1: Uuid = "65b8563978d7433085c639502b2f9b01".parse().unwrap(); /// let uuid2: Uuid = "65b85639-78d7-4330-85c6-39502b2f9b01".parse().unwrap(); /// assert_eq!(uuid1, uuid2); /// ``` pub fn parse_str(src: &str) -> Result { use std::alloc::{alloc, Layout}; let uuid: *mut [u8; 16] = unsafe { alloc(Layout::new::<[u8; 16]>()) as *mut [u8; 16] }; if src.len() == 36 { // Unfortunately the manpage of `uuid_parse(3)` states that it technically requiers a // terminating null byte at the end, which we don't have, so do this manually: let uuid: &mut [u8] = unsafe { &mut (*uuid)[..] }; let src = src.as_bytes(); if src[8] != b'-' || src[13] != b'-' || src[18] != b'-' || src[23] != b'-' { return Err(UuidError); } for i in 0..4 { uuid[i] = hex_digit(src[2 * i])? << 4 | hex_digit(src[2 * i + 1])?; } for i in 4..6 { uuid[i] = hex_digit(src[2 * i + 1])? << 4 | hex_digit(src[2 * i + 2])?; } for i in 6..8 { uuid[i] = hex_digit(src[2 * i + 2])? << 4 | hex_digit(src[2 * i + 3])?; } for i in 8..10 { uuid[i] = hex_digit(src[2 * i + 3])? << 4 | hex_digit(src[2 * i + 4])?; } for i in 10..16 { uuid[i] = hex_digit(src[2 * i + 4])? << 4 | hex_digit(src[2 * i + 5])?; } } else if src.len() == 32 { let uuid: &mut [u8] = unsafe { &mut (*uuid)[..] }; let src = src.as_bytes(); for i in 0..16 { uuid[i] = hex_digit(src[2 * i])? << 4 | hex_digit(src[2 * i + 1])?; } } else { return Err(UuidError); } Ok(Self(unsafe { Box::from_raw(uuid) })) } } impl AsRef<[u8]> for Uuid { #[inline] fn as_ref(&self) -> &[u8] { self.as_bytes() } } impl AsMut<[u8]> for Uuid { #[inline] fn as_mut(&mut self) -> &mut [u8] { &mut (self.0)[..] } } impl Borrow<[u8]> for Uuid { #[inline] fn borrow(&self) -> &[u8] { &(self.0)[..] } } impl BorrowMut<[u8]> for Uuid { #[inline] fn borrow_mut(&mut self) -> &mut [u8] { &mut (self.0)[..] } } impl From<[u8; 16]> for Uuid { fn from(data: [u8; 16]) -> Self { Self(Box::new(data)) } } impl From> for Uuid { fn from(data: Box<[u8; 16]>) -> Self { Self(data) } } impl From for [u8; 16] { fn from(this: Uuid) -> [u8; 16] { *this.0 } } impl fmt::Display for Uuid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) } } impl std::str::FromStr for Uuid { type Err = UuidError; fn from_str(src: &str) -> Result { Self::parse_str(src) } } //forward_deserialize_to_from_str!(Uuid); #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for Uuid { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { use serde::de::Error; struct ForwardToStrVisitor; impl<'a> serde::de::Visitor<'a> for ForwardToStrVisitor { type Value = Uuid; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("a valid uuid as a string") } fn visit_str(self, v: &str) -> Result { v.parse::() .map_err(|err| Error::custom(err.to_string())) } } deserializer.deserialize_str(ForwardToStrVisitor) } } #[test] fn test_uuid() { let uuid = Uuid::generate(); let ser: String = uuid.to_string(); let de: Uuid = ser.parse().expect("failed to parse uuid"); assert_eq!(uuid, de); } #[cfg(feature = "serde")] #[test] fn test_uuid_serde() { let uuid = Uuid::generate(); let ser: String = serde_json::to_string(&uuid).expect("failed to serialize uuid"); let de: Uuid = serde_json::from_str(&ser).expect("failed to deserialize uuid"); assert_eq!(uuid, de); }