use crate::ffi::uuid as ffi;
use std::convert::{TryFrom, TryInto};
use std::io::Write;
use crate::msgpack;
use crate::msgpack::{Context, Decode, DecodeError, Encode, EncodeError};
pub use ::uuid::{adapter, Error};
use serde::{Deserialize, Serialize};
type Inner = ::uuid::Uuid;
#[derive(Debug, Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd, Default)]
pub struct Uuid {
inner: Inner,
}
impl Uuid {
#[inline(always)]
pub fn random() -> Self {
unsafe {
let mut tt = std::mem::MaybeUninit::uninit();
ffi::tt_uuid_create(tt.as_mut_ptr());
Self::from_tt_uuid(tt.assume_init())
}
}
#[inline(always)]
pub fn from_inner(inner: Inner) -> Self {
inner.into()
}
#[inline(always)]
pub fn into_inner(self) -> Inner {
self.into()
}
#[inline(always)]
pub fn from_bytes(bytes: [u8; 16]) -> Self {
Inner::from_bytes(bytes).into()
}
#[inline(always)]
pub fn try_from_slice(bytes: &[u8]) -> Option<Self> {
std::convert::TryInto::try_into(bytes)
.ok()
.map(Self::from_bytes)
}
#[inline(always)]
pub fn from_tt_uuid(mut tt: ffi::tt_uuid) -> Self {
unsafe {
tt.tl = tt.tl.swap_bytes();
tt.tm = tt.tm.swap_bytes();
tt.th = tt.th.swap_bytes();
let bytes: [u8; 16] = std::mem::transmute(tt);
Self::from_bytes(bytes)
}
}
#[inline(always)]
pub fn to_tt_uuid(&self) -> ffi::tt_uuid {
unsafe {
let mut tt: ffi::tt_uuid = std::mem::transmute(*self.inner.as_bytes());
tt.tl = tt.tl.swap_bytes();
tt.tm = tt.tm.swap_bytes();
tt.th = tt.th.swap_bytes();
tt
}
}
#[inline(always)]
pub fn as_bytes(&self) -> &[u8; 16] {
self.inner.as_bytes()
}
#[inline(always)]
pub fn nil() -> Self {
Inner::nil().into()
}
#[inline(always)]
pub fn is_nil(&self) -> bool {
self.inner.is_nil()
}
#[inline(always)]
pub fn parse_str(input: &str) -> Result<Self, Error> {
Inner::parse_str(input).map(Self::from)
}
#[inline(always)]
pub const fn to_hyphenated(self) -> adapter::Hyphenated {
self.inner.to_hyphenated()
}
#[inline(always)]
pub const fn to_hyphenated_ref(&self) -> adapter::HyphenatedRef<'_> {
self.inner.to_hyphenated_ref()
}
#[inline(always)]
pub const fn to_simple(self) -> adapter::Simple {
self.inner.to_simple()
}
#[inline(always)]
pub const fn to_simple_ref(&self) -> adapter::SimpleRef<'_> {
self.inner.to_simple_ref()
}
#[inline(always)]
pub const fn to_urn(self) -> adapter::Urn {
self.inner.to_urn()
}
#[inline(always)]
pub const fn to_urn_ref(&self) -> adapter::UrnRef<'_> {
self.inner.to_urn_ref()
}
fn from_ext_structure(tag: i8, bytes: &[u8]) -> Result<Self, String> {
if tag != ffi::MP_UUID {
return Err(format!("Expected UUID, found msgpack ext #{}", tag));
}
Self::try_from_slice(bytes).ok_or_else(|| {
format!(
"Not enough bytes for UUID: expected 16, got {}",
bytes.len()
)
})
}
}
impl<'a> TryFrom<msgpack::ExtStruct<'a>> for Uuid {
type Error = String;
#[inline(always)]
fn try_from(value: msgpack::ExtStruct<'a>) -> Result<Self, Self::Error> {
Self::from_ext_structure(value.tag, value.data)
}
}
impl From<Inner> for Uuid {
#[inline(always)]
fn from(inner: Inner) -> Self {
Self { inner }
}
}
impl From<Uuid> for Inner {
#[inline(always)]
fn from(uuid: Uuid) -> Self {
uuid.inner
}
}
impl std::fmt::Display for Uuid {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl std::fmt::LowerHex for Uuid {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::LowerHex::fmt(&self.inner, f)
}
}
impl std::fmt::UpperHex for Uuid {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::UpperHex::fmt(&self.inner, f)
}
}
impl std::str::FromStr for Uuid {
type Err = Error;
fn from_str(uuid_str: &str) -> Result<Self, Self::Err> {
Self::parse_str(uuid_str)
}
}
impl Encode for Uuid {
fn encode(&self, w: &mut impl Write, context: &Context) -> Result<(), EncodeError> {
msgpack::ExtStruct::new(ffi::MP_UUID, self.as_bytes()).encode(w, context)
}
}
impl<'de> Decode<'de> for Uuid {
fn decode(r: &mut &'de [u8], context: &Context) -> Result<Self, DecodeError> {
msgpack::ExtStruct::decode(r, context)?
.try_into()
.map_err(DecodeError::new::<Self>)
}
}
impl serde::Serialize for Uuid {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
#[derive(Serialize)]
struct _ExtStruct<'a>((i8, &'a serde_bytes::Bytes));
let data = self.as_bytes();
_ExtStruct((ffi::MP_UUID, serde_bytes::Bytes::new(data))).serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for Uuid {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
struct _ExtStruct((i8, serde_bytes::ByteBuf));
let _ExtStruct((tag, bytes)) = Deserialize::deserialize(deserializer)?;
Self::from_ext_structure(tag, bytes.as_slice()).map_err(serde::de::Error::custom)
}
}
static mut CTID_UUID: Option<u32> = None;
fn ctid_uuid() -> u32 {
unsafe {
if (*std::ptr::addr_of!(CTID_UUID)).is_none() {
let lua = crate::global_lua();
let ctid_uuid =
tlua::ffi::luaL_ctypeid(tlua::AsLua::as_lua(&lua), crate::c_ptr!("struct tt_uuid"));
assert!(ctid_uuid != 0);
CTID_UUID = Some(ctid_uuid)
}
CTID_UUID.unwrap()
}
}
unsafe impl tlua::AsCData for ffi::tt_uuid {
fn ctypeid() -> tlua::ffi::CTypeID {
ctid_uuid()
}
}
impl<L> tlua::LuaRead<L> for Uuid
where
L: tlua::AsLua,
{
fn lua_read_at_position(lua: L, index: std::num::NonZeroI32) -> tlua::ReadResult<Self, L> {
let tlua::CData(uuid) = lua.read_at_nz(index)?;
Ok(Self::from_tt_uuid(uuid))
}
}
impl<L: tlua::AsLua> tlua::Push<L> for Uuid {
type Err = tlua::Void;
#[inline(always)]
fn push_to_lua(&self, lua: L) -> Result<tlua::PushGuard<L>, (Self::Err, L)> {
Ok(lua.push_one(tlua::CData(self.to_tt_uuid())))
}
}
impl<L: tlua::AsLua> tlua::PushOne<L> for Uuid {}
impl<L: tlua::AsLua> tlua::PushInto<L> for Uuid {
type Err = tlua::Void;
fn push_into_lua(self, lua: L) -> Result<tlua::PushGuard<L>, (Self::Err, L)> {
Ok(lua.push_one(tlua::CData(self.to_tt_uuid())))
}
}
impl<L: tlua::AsLua> tlua::PushOneInto<L> for Uuid {}
#[cfg(test)]
mod tests {
use super::*;
use crate::msgpack;
#[test]
fn serialize() {
let result = [
216, 2, 227, 6, 158, 228, 241, 69, 78, 73, 160, 56, 166, 128, 14, 42, 161, 217,
];
let uuid = Uuid::parse_str("e3069ee4-f145-4e49-a038-a6800e2aa1d9").unwrap();
let serde_data = rmp_serde::to_vec(&uuid).unwrap();
assert_eq!(serde_data, result);
let msgpack_data = msgpack::encode(&uuid);
assert_eq!(msgpack_data, result);
}
#[test]
fn deserialize() {
let uuid = Uuid {
inner: uuid::Uuid::NAMESPACE_DNS,
};
let serde_data = rmp_serde::to_vec(&uuid).unwrap();
let msgpack_uuid = msgpack::decode(serde_data.as_slice()).unwrap();
assert_eq!(uuid, msgpack_uuid);
let serde_uuid = rmp_serde::from_slice(serde_data.as_slice()).unwrap();
assert_eq!(uuid, serde_uuid);
}
}