use crate::{bytes::*, Error};
use core::fmt;
const BASE_UUID: [u8; 16] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 00, 0x80, 0x00,
0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
];
#[derive(PartialEq, Eq, Copy, Clone)]
pub struct Uuid16(pub u16);
#[derive(PartialEq, Eq, Copy, Clone)]
pub struct Uuid32(pub u32);
#[derive(PartialEq, Eq, Copy, Clone)]
pub struct Uuid128([u8; 16]);
impl Uuid128 {
pub const fn from_bytes(bytes: [u8; 16]) -> Self {
Self(bytes)
}
pub const fn parse_static(s: &'static str) -> Self {
const fn parse_nibble(nibble: u8) -> u8 {
let hex_digit_out_of_range = 1;
match nibble {
b'0'..=b'9' => nibble - b'0',
b'a'..=b'f' => nibble - b'a' + 10,
_ => [0][hex_digit_out_of_range],
}
}
let expected_dash = 1;
let unexpected_trailing_data = 1;
let mut index = 0;
let mut bytes = [0; 16];
macro_rules! eat_byte {
($s:ident[$i:ident..]) => {{
let hi = parse_nibble($s.as_bytes()[$i]);
$i += 1;
let lo = parse_nibble($s.as_bytes()[$i]);
$i += 1;
(hi << 4) | lo
}};
}
macro_rules! eat_dash {
($s:ident[$i:ident..]) => {{
match $s.as_bytes()[$i] {
b'-' => {}
_ => [()][expected_dash],
}
$i += 1;
}};
}
bytes[0] = eat_byte!(s[index..]);
bytes[1] = eat_byte!(s[index..]);
bytes[2] = eat_byte!(s[index..]);
bytes[3] = eat_byte!(s[index..]);
eat_dash!(s[index..]);
bytes[4] = eat_byte!(s[index..]);
bytes[5] = eat_byte!(s[index..]);
eat_dash!(s[index..]);
bytes[6] = eat_byte!(s[index..]);
bytes[7] = eat_byte!(s[index..]);
eat_dash!(s[index..]);
bytes[8] = eat_byte!(s[index..]);
bytes[9] = eat_byte!(s[index..]);
eat_dash!(s[index..]);
bytes[10] = eat_byte!(s[index..]);
bytes[11] = eat_byte!(s[index..]);
bytes[12] = eat_byte!(s[index..]);
bytes[13] = eat_byte!(s[index..]);
bytes[14] = eat_byte!(s[index..]);
bytes[15] = eat_byte!(s[index..]);
if s.len() > index {
[()][unexpected_trailing_data];
}
Uuid128(bytes)
}
}
impl From<Uuid16> for Uuid32 {
fn from(smol: Uuid16) -> Self {
Uuid32(smol.0.into())
}
}
impl From<Uuid16> for Uuid128 {
fn from(uuid: Uuid16) -> Self {
Uuid32::from(uuid).into()
}
}
impl From<Uuid32> for Uuid128 {
fn from(uuid: Uuid32) -> Self {
let mut buf = BASE_UUID;
buf[..4].copy_from_slice(&uuid.0.to_be_bytes());
Uuid128(buf)
}
}
impl ToBytes for Uuid16 {
fn to_bytes(&self, buffer: &mut ByteWriter<'_>) -> Result<(), Error> {
buffer.write_slice(&self.0.to_le_bytes())
}
}
impl ToBytes for Uuid32 {
fn to_bytes(&self, buffer: &mut ByteWriter<'_>) -> Result<(), Error> {
buffer.write_slice(&self.0.to_le_bytes())
}
}
impl ToBytes for Uuid128 {
fn to_bytes(&self, buffer: &mut ByteWriter<'_>) -> Result<(), Error> {
buffer.write_slice(&self.0)
}
}
impl FromBytes<'_> for Uuid16 {
fn from_bytes(bytes: &mut ByteReader<'_>) -> Result<Self, Error> {
let array = bytes.read_array()?;
Ok(Uuid16(u16::from_le_bytes(array)))
}
}
impl FromBytes<'_> for Uuid32 {
fn from_bytes(bytes: &mut ByteReader<'_>) -> Result<Self, Error> {
let array = bytes.read_array()?;
Ok(Uuid32(u32::from_le_bytes(array)))
}
}
impl FromBytes<'_> for Uuid128 {
fn from_bytes(bytes: &mut ByteReader<'_>) -> Result<Self, Error> {
let array = bytes.read_array()?;
Ok(Uuid128(array))
}
}
impl fmt::Debug for Uuid16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Uuid16({:04x})", self.0)
}
}
impl fmt::Debug for Uuid32 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Uuid32({:08x})", self.0)
}
}
impl fmt::Debug for Uuid128 {
#[allow(clippy::many_single_char_names, clippy::just_underscores_and_digits)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let [_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15] = self.0;
let a = u32::from_be_bytes([_0, _1, _2, _3]);
let b = u16::from_be_bytes([_4, _5]);
let c = u16::from_be_bytes([_6, _7]);
let d = u16::from_be_bytes([_8, _9]);
let e = u64::from_be_bytes([0, 0, _10, _11, _12, _13, _14, _15]);
write!(f, "{:08x}-{:04x}-{:04x}-{:04x}-{:012x}", a, b, c, d, e)
}
}
#[derive(Debug, Copy, Clone)]
pub enum UuidKind {
Uuid16,
Uuid32,
Uuid128,
}
pub trait IsUuid: for<'a> FromBytes<'a> + ToBytes + Copy {
const KIND: UuidKind;
}
impl IsUuid for Uuid16 {
const KIND: UuidKind = UuidKind::Uuid16;
}
impl IsUuid for Uuid32 {
const KIND: UuidKind = UuidKind::Uuid32;
}
impl IsUuid for Uuid128 {
const KIND: UuidKind = UuidKind::Uuid128;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fmt() {
let uuid = Uuid128::from_bytes([
0x02, 0x3e, 0x45, 0x67, 0x08, 0x9b, 0x02, 0xd3, 0x04, 0x56, 0x00, 0x66, 0x14, 0x17,
0x40, 0x00,
]);
assert_eq!(
format!("{:?}", uuid),
"023e4567-089b-02d3-0456-006614174000"
);
}
#[test]
fn convert() {
let uuid = 0xfd6f; let uuid = Uuid128::from(Uuid16(uuid));
assert_eq!(
format!("{:?}", uuid),
"0000fd6f-0000-1000-8000-00805f9b34fb"
);
}
#[test]
fn parse() {
let uuid = "0000fd6f-0000-1000-8000-00805f9b34fb";
assert_eq!(format!("{:?}", Uuid128::parse_static(uuid)), uuid);
}
}