use std::fmt;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use uuid::{NoContext, Timestamp, Uuid};
use crate::crypto::ids::IdError;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct UuidV7(Uuid);
impl UuidV7 {
pub fn generate() -> Self {
let ts = Timestamp::now(NoContext);
Self(Uuid::new_v7(ts))
}
pub fn as_uuid(&self) -> &Uuid {
&self.0
}
pub fn as_bytes(&self) -> &[u8; 16] {
self.0.as_bytes()
}
}
impl fmt::Display for UuidV7 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl FromStr for UuidV7 {
type Err = IdError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let uuid = Uuid::parse_str(s)
.map_err(|_| IdError::InvalidFormat("uuid_v7", "invalid UUID format"))?;
if uuid.get_version_num() != 7 {
return Err(IdError::InvalidFormat("uuid_v7", "not a v7 UUID"));
}
Ok(Self(uuid))
}
}
impl From<Uuid> for UuidV7 {
fn from(u: Uuid) -> Self {
Self(u)
}
}
impl AsRef<Uuid> for UuidV7 {
fn as_ref(&self) -> &Uuid {
&self.0
}
}
#[cfg(feature = "crypto-sqlx")]
mod sqlx_impl {
use super::UuidV7;
use sqlx::{
encode::IsNull,
error::BoxDynError,
postgres::{PgArgumentBuffer, PgTypeInfo, PgValueRef},
};
use uuid::Uuid;
impl sqlx::Type<sqlx::Postgres> for UuidV7 {
fn type_info() -> PgTypeInfo {
<Uuid as sqlx::Type<sqlx::Postgres>>::type_info()
}
}
impl<'q> sqlx::Encode<'q, sqlx::Postgres> for UuidV7 {
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
<Uuid as sqlx::Encode<'q, sqlx::Postgres>>::encode_by_ref(&self.0, buf)
}
}
impl<'r> sqlx::Decode<'r, sqlx::Postgres> for UuidV7 {
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
let uuid = <Uuid as sqlx::Decode<sqlx::Postgres>>::decode(value)?;
Ok(Self(uuid))
}
}
}