mirror of
https://git.proxmox.com/git/proxmox
synced 2025-05-29 00:56:52 +00:00
235 lines
6.4 KiB
Rust
235 lines
6.4 KiB
Rust
//! 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<u8, UuidError> {
|
|
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<Self, UuidError> {
|
|
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<Box<[u8; 16]>> for Uuid {
|
|
fn from(data: Box<[u8; 16]>) -> Self {
|
|
Self(data)
|
|
}
|
|
}
|
|
|
|
impl From<Uuid> 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, UuidError> {
|
|
Self::parse_str(src)
|
|
}
|
|
}
|
|
|
|
//forward_deserialize_to_from_str!(Uuid);
|
|
#[cfg(feature = "serde")]
|
|
impl<'de> serde::Deserialize<'de> for Uuid {
|
|
fn deserialize<D>(deserializer: D) -> Result<Uuid, D::Error>
|
|
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<E: Error>(self, v: &str) -> Result<Uuid, E> {
|
|
v.parse::<Uuid>()
|
|
.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);
|
|
}
|