use core::fmt;
#[repr(C)]
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Clsid {
pub data1: u32,
pub data2: u16,
pub data3: u16,
pub data4: [u8; 8],
}
impl Clsid {
pub const NIL: Self = Self::from_parts(0, 0, 0, [0; 8]);
#[inline]
#[must_use]
pub const fn from_parts(data1: u32, data2: u16, data3: u16, data4: [u8; 8]) -> Self {
Self {
data1,
data2,
data3,
data4,
}
}
#[inline]
#[must_use]
pub const fn from_u128(value: u128) -> Self {
let b = value.to_be_bytes();
Self {
data1: u32::from_be_bytes([b[0], b[1], b[2], b[3]]),
data2: u16::from_be_bytes([b[4], b[5]]),
data3: u16::from_be_bytes([b[6], b[7]]),
data4: [b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]],
}
}
#[inline]
#[must_use]
pub const fn to_u128(&self) -> u128 {
u128::from_be_bytes([
(self.data1 >> 24) as u8,
(self.data1 >> 16) as u8,
(self.data1 >> 8) as u8,
self.data1 as u8,
(self.data2 >> 8) as u8,
self.data2 as u8,
(self.data3 >> 8) as u8,
self.data3 as u8,
self.data4[0],
self.data4[1],
self.data4[2],
self.data4[3],
self.data4[4],
self.data4[5],
self.data4[6],
self.data4[7],
])
}
#[inline]
#[must_use]
pub const fn is_nil(&self) -> bool {
self.to_u128() == 0
}
}
impl fmt::Debug for Clsid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Clsid({self})")
}
}
impl fmt::Display for Clsid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{{{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}",
self.data1,
self.data2,
self.data3,
self.data4[0],
self.data4[1],
self.data4[2],
self.data4[3],
self.data4[4],
self.data4[5],
self.data4[6],
self.data4[7],
)
}
}
#[cfg(windows)]
impl From<windows_core::GUID> for Clsid {
#[inline]
fn from(g: windows_core::GUID) -> Self {
Self {
data1: g.data1,
data2: g.data2,
data3: g.data3,
data4: g.data4,
}
}
}
#[cfg(windows)]
impl From<Clsid> for windows_core::GUID {
#[inline]
fn from(c: Clsid) -> Self {
Self {
data1: c.data1,
data2: c.data2,
data3: c.data3,
data4: c.data4,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nil_is_nil() {
assert!(Clsid::NIL.is_nil());
assert_eq!(Clsid::NIL.to_u128(), 0);
}
#[test]
fn non_nil_is_not_nil() {
assert!(!Clsid::from_u128(1).is_nil());
assert!(!Clsid::from_parts(0, 0, 0, [0, 0, 0, 0, 0, 0, 0, 1]).is_nil());
}
#[test]
fn from_u128_round_trips() {
let raw = 0x12345678_9abc_def0_1234_56789abcdef0_u128;
let c = Clsid::from_u128(raw);
assert_eq!(c.to_u128(), raw);
}
#[test]
fn from_parts_matches_explicit_fields() {
let c = Clsid::from_parts(0xDEAD_BEEF, 0x1234, 0x5678, [1, 2, 3, 4, 5, 6, 7, 8]);
assert_eq!(c.data1, 0xDEAD_BEEF);
assert_eq!(c.data2, 0x1234);
assert_eq!(c.data3, 0x5678);
assert_eq!(c.data4, [1, 2, 3, 4, 5, 6, 7, 8]);
}
#[test]
fn from_u128_matches_known_byte_order() {
let c = Clsid::from_u128(0x00000000_0000_0000_C000_000000000046);
assert_eq!(c.data1, 0);
assert_eq!(c.data2, 0);
assert_eq!(c.data3, 0);
assert_eq!(c.data4, [0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46]);
}
#[test]
fn display_format_is_canonical() {
let c = Clsid::from_u128(0x12345678_9abc_def0_a1b2_c3d4e5f60718);
assert_eq!(format!("{c}"), "{12345678-9ABC-DEF0-A1B2-C3D4E5F60718}");
}
#[test]
fn debug_wraps_display() {
let c = Clsid::from_u128(1);
assert_eq!(
format!("{c:?}"),
"Clsid({00000000-0000-0000-0000-000000000001})"
);
}
#[test]
fn equality_is_structural() {
let a = Clsid::from_u128(0x42);
let b = Clsid::from_u128(0x42);
let c = Clsid::from_u128(0x43);
assert_eq!(a, b);
assert_ne!(a, c);
}
#[cfg(windows)]
#[test]
fn from_windows_guid_preserves_bytes() {
let g = windows_core::GUID {
data1: 0xDEAD_BEEF,
data2: 0x1234,
data3: 0x5678,
data4: [1, 2, 3, 4, 5, 6, 7, 8],
};
let c: Clsid = g.into();
assert_eq!(c.data1, 0xDEAD_BEEF);
assert_eq!(c.data2, 0x1234);
assert_eq!(c.data3, 0x5678);
assert_eq!(c.data4, [1, 2, 3, 4, 5, 6, 7, 8]);
}
#[cfg(windows)]
#[test]
fn windows_guid_round_trips() {
let g = windows_core::GUID {
data1: 0x12345678,
data2: 0x9abc,
data3: 0xdef0,
data4: [0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf6, 0x07, 0x18],
};
let c: Clsid = g.into();
let g2: windows_core::GUID = c.into();
assert_eq!(g, g2);
}
#[cfg(windows)]
#[test]
fn clsid_layout_matches_windows_guid() {
use core::mem::{align_of, size_of};
assert_eq!(size_of::<Clsid>(), size_of::<windows_core::GUID>());
assert_eq!(align_of::<Clsid>(), align_of::<windows_core::GUID>());
}
}