//! Dealing with type IDs of states
#![deny(unsafe_code)]
use std::fmt::{Debug, Display, Formatter};
use std::{fmt, io, ptr};
/// A Globally Unique Identifier (GUID)
///
/// This is used to (optionally) specify type IDs for states.
/// It provides [`From<T>`] implementations for various types `T` from foreign crates that also represent GUIDs.
/// These implementations are available when the respective Cargo features are enabled:
/// - Feature `windows`: [`From<windows::core::GUID>`](https://docs.rs/windows/latest/windows/core/struct.GUID.html)
/// - Feature `winapi`:
/// [`From<winapi::shared::guiddef::GUID>`](https://docs.rs/winapi/latest/winapi/shared/guiddef/struct.GUID.html)
/// - Feature `uuid`: [`From<uuid::Uuid>`](https://docs.rs/uuid/1/uuid)
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct GUID(windows::core::GUID);
impl GUID {
/// Creates a unique GUID value
///
/// # Errors
/// Returns an error if creating the GUID fails
pub fn new() -> io::Result<Self> {
Ok(Self(windows::core::GUID::new()?))
}
/// Creates a GUID represented by the all-zero byte pattern
pub const fn zeroed() -> Self {
Self(windows::core::GUID::zeroed())
}
/// Creates a GUID with the given constant values
pub const fn from_values(data1: u32, data2: u16, data3: u16, data4: [u8; 8]) -> Self {
Self(windows::core::GUID::from_values(data1, data2, data3, data4))
}
/// Creates a GUID from a [`u128`] value
pub const fn from_u128(uuid: u128) -> Self {
Self(windows::core::GUID::from_u128(uuid))
}
/// Converts a GUID to a [`u128`] value
pub const fn to_u128(&self) -> u128 {
self.0.to_u128()
}
}
impl From<&str> for GUID {
fn from(value: &str) -> Self {
Self(value.into())
}
}
impl From<u128> for GUID {
fn from(value: u128) -> Self {
Self(value.into())
}
}
impl From<GUID> for u128 {
fn from(value: GUID) -> Self {
value.to_u128()
}
}
/// Converting between [`GUID`] and [`windows::core::GUID`](https://docs.rs/windows/0/windows/core/struct.GUID.html)
#[cfg(feature = "windows")]
mod impl_windows {
use super::*;
impl From<GUID> for windows::core::GUID {
fn from(guid: GUID) -> Self {
guid.0
}
}
impl From<windows::core::GUID> for GUID {
fn from(guid: windows::core::GUID) -> Self {
Self(guid)
}
}
}
/// Converting between [`GUID`] and
/// [`winapi::shared::guiddef::GUID`](https://docs.rs/winapi/0/winapi/shared/guiddef/struct.GUID.html)
#[cfg(feature = "winapi")]
mod impl_winapi {
use super::*;
impl From<GUID> for winapi::shared::guiddef::GUID {
fn from(guid: GUID) -> Self {
Self {
Data1: guid.0.data1,
Data2: guid.0.data2,
Data3: guid.0.data3,
Data4: guid.0.data4,
}
}
}
impl From<winapi::shared::guiddef::GUID> for GUID {
fn from(guid: winapi::shared::guiddef::GUID) -> Self {
Self::from_values(guid.Data1, guid.Data2, guid.Data3, guid.Data4)
}
}
}
/// Converting between [`GUID`] and [`uuid::Uuid`](https://docs.rs/uuid/1/uuid/struct.Uuid.html)
#[cfg(feature = "uuid")]
mod impl_uuid {
use super::*;
impl From<GUID> for uuid::Uuid {
fn from(guid: GUID) -> Self {
Self::from_u128(guid.into())
}
}
impl From<uuid::Uuid> for GUID {
fn from(uuid: uuid::Uuid) -> Self {
uuid.as_u128().into()
}
}
}
/// Internal helper type wrapping an optional GUID for use as a type ID of a state
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub(crate) struct TypeId(Option<windows::core::GUID>);
impl TypeId {
/// Creates a [`TypeId`] containing no [`GUID`] for use in an untyped state
pub(crate) const fn none() -> Self {
Self(None)
}
/// Creates a [`TypeId`] containing the given [`GUID`]
pub(crate) const fn from_guid(guid: GUID) -> Self {
Self(Some(guid.0))
}
/// Returns a raw pointer to the underlying [`GUID`], or a null pointer if there is none
///
/// It is guaranteed that the returned pointer is either a null pointer or points to a valid [`GUID`] as long the
/// instance of [`TypeId`] is live. The returned pointer can be passed to WNF APIs expecting an optional type id.
pub(crate) const fn as_ptr(&self) -> *const windows::core::GUID {
match self.0.as_ref() {
Some(guid) => guid,
None => ptr::null(),
}
}
}
impl From<GUID> for TypeId {
fn from(guid: GUID) -> Self {
Self(Some(guid.0))
}
}
impl Display for TypeId {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.0, f)
}
}
#[cfg(test)]
mod tests {
#![allow(unsafe_code)]
use super::*;
#[test]
fn type_id_from_guid_as_ptr() {
let guid: GUID = GUID::from_u128(0x0011_2233_4455_6677_8899_AABB_CCDD_EEFF);
let type_id: TypeId = guid.into();
let ptr = type_id.as_ptr();
assert!(!ptr.is_null());
// SAFETY:
// `ptr` points to a valid GUID by the safety conditions of `TypeId::as_ptr` because `!ptr.is_null()` and
// `type_id` is live
let windows_guid = unsafe { *ptr };
assert_eq!(windows_guid.to_u128(), guid.to_u128());
}
#[test]
fn type_id_none_as_ptr() {
let type_id = TypeId::none();
let ptr = type_id.as_ptr();
assert!(ptr.is_null());
}
}