use std::fmt;
use std::hash::Hash;
use std::str::FromStr;
pub trait EntityIdType:
Copy + Clone + Eq + Hash + fmt::Debug + fmt::Display + FromStr + Send + Sync + 'static
{
const ENTITY_NAME: &'static str;
fn new(id: u128) -> Self;
fn as_u128(&self) -> u128;
fn now_v7() -> Self;
fn now_v7_with_clock(clock: &dyn crate::store::Clock) -> Self {
Self::new(generate_v7_id_with_clock(clock))
}
fn nil() -> Self;
}
pub fn generate_v7_id() -> u128 {
uuid::Uuid::now_v7().as_u128()
}
pub fn generate_v7_id_with_clock(clock: &dyn crate::store::Clock) -> u128 {
let timestamp_us = clock.now_us().max(0);
let seconds = timestamp_us / 1_000_000;
let subsec_nanos = (timestamp_us % 1_000_000) * 1_000;
let seconds = u64::try_from(seconds).unwrap_or(u64::MAX);
let subsec_nanos = u32::try_from(subsec_nanos).unwrap_or(u32::MAX);
uuid::Uuid::new_v7(uuid::Timestamp::from_unix(
uuid::NoContext,
seconds,
subsec_nanos,
))
.as_u128()
}
#[macro_export]
macro_rules! define_entity_id {
($name:ident, $entity:literal) => {
#[doc = concat!("Typed entity ID for `", $entity, "` entities. Wraps a `u128` UUIDv7.")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct $name(u128);
impl $crate::id::EntityIdType for $name {
const ENTITY_NAME: &'static str = $entity;
fn new(id: u128) -> Self {
Self(id)
}
fn as_u128(&self) -> u128 {
self.0
}
fn now_v7() -> Self {
Self($crate::id::generate_v7_id())
}
fn now_v7_with_clock(clock: &dyn $crate::store::Clock) -> Self {
Self($crate::id::generate_v7_id_with_clock(clock))
}
fn nil() -> Self {
Self(0)
}
}
impl ::std::fmt::Display for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "{}:{:032x}", $entity, self.0)
}
}
impl ::std::str::FromStr for $name {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let hex = s.strip_prefix(concat!($entity, ":")).ok_or_else(|| {
format!(
"invalid {}: missing entity prefix '{}:' in {s:?}",
$entity, $entity
)
})?;
u128::from_str_radix(hex, 16)
.map(Self)
.map_err(|e| format!("invalid {}: {e}", $entity))
}
}
};
}
define_entity_id!(EventId, "event");
define_entity_id!(CorrelationId, "correlation");
define_entity_id!(CausationId, "causation");
define_entity_id!(IdempotencyKey, "idempotency");
impl From<u128> for EventId {
fn from(id: u128) -> Self {
<Self as EntityIdType>::new(id)
}
}
impl From<EventId> for u128 {
fn from(id: EventId) -> Self {
id.as_u128()
}
}
impl From<u128> for CorrelationId {
fn from(id: u128) -> Self {
<Self as EntityIdType>::new(id)
}
}
impl From<CorrelationId> for u128 {
fn from(id: CorrelationId) -> Self {
id.as_u128()
}
}
impl From<u128> for CausationId {
fn from(id: u128) -> Self {
<Self as EntityIdType>::new(id)
}
}
impl From<CausationId> for u128 {
fn from(id: CausationId) -> Self {
id.as_u128()
}
}
impl From<u128> for IdempotencyKey {
fn from(id: u128) -> Self {
<Self as EntityIdType>::new(id)
}
}
impl From<IdempotencyKey> for u128 {
fn from(id: IdempotencyKey) -> Self {
id.as_u128()
}
}
impl serde::Serialize for EventId {
fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
crate::wire::u128_bytes::serialize(&self.0, ser)
}
}
impl<'de> serde::Deserialize<'de> for EventId {
fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
crate::wire::u128_bytes::deserialize(de).map(Self)
}
}
impl serde::Serialize for CorrelationId {
fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
crate::wire::u128_bytes::serialize(&self.0, ser)
}
}
impl<'de> serde::Deserialize<'de> for CorrelationId {
fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
crate::wire::u128_bytes::deserialize(de).map(Self)
}
}
impl serde::Serialize for CausationId {
fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
crate::wire::u128_bytes::serialize(&self.0, ser)
}
}
impl<'de> serde::Deserialize<'de> for CausationId {
fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
crate::wire::u128_bytes::deserialize(de).map(Self)
}
}
impl serde::Serialize for IdempotencyKey {
fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
crate::wire::u128_bytes::serialize(&self.0, ser)
}
}
impl<'de> serde::Deserialize<'de> for IdempotencyKey {
fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
crate::wire::u128_bytes::deserialize(de).map(Self)
}
}