use std::fmt;
use uuid::Uuid;
macro_rules! define_id {
($name:ident, $doc:literal) => {
#[doc = $doc]
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
pub struct $name(Uuid);
impl $name {
#[must_use]
pub fn new() -> Self {
Self(Uuid::new_v4())
}
#[must_use]
pub fn from_uuid(u: Uuid) -> Self {
Self(u)
}
#[must_use]
pub fn as_uuid(self) -> Uuid {
self.0
}
}
impl Default for $name {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl From<Uuid> for $name {
fn from(u: Uuid) -> Self {
Self(u)
}
}
};
}
define_id!(
EventId,
"Globally unique identifier for a single persisted event."
);
define_id!(
CorrelationId,
"Groups all events and commands that originate from the same root operation."
);
impl CorrelationId {
#[must_use]
pub fn from_interchange_ref(interchange_ref: &str) -> Self {
const INTERCHANGE_NS: Uuid = Uuid::from_u128(0xa3c7_e1f0_5b2d_4e80_9f6a_1b3c_5d7e_9a0b);
Self(Uuid::new_v5(&INTERCHANGE_NS, interchange_ref.as_bytes()))
}
}
define_id!(
CausationId,
"Points to the event or command that directly caused this event."
);
define_id!(
ConversationId,
"Links events that belong to the same business conversation \
(e.g. a UTILMD exchange and its APERAK acknowledgement)."
);
define_id!(
ProcessId,
"Stable identifier for a single MaKo process instance."
);
define_id!(
TenantId,
"Scopes all streams and events to a single market participant or deployment tenant."
);
impl TenantId {
#[must_use]
pub fn from_party_id(party_id: &str) -> Self {
const TENANT_NS: Uuid = Uuid::from_u128(0x7e4a_6b1c_2d3e_5f60_8a9b_0c1d_2e3f_4a5b);
Self(Uuid::new_v5(&TENANT_NS, party_id.as_bytes()))
}
}
define_id!(
OutboxMessageId,
"Unique identifier for a single outbox message entry."
);
define_id!(
DeadlineId,
"Unique identifier for a registered process deadline."
);
impl From<EventId> for CausationId {
fn from(id: EventId) -> Self {
Self(id.0)
}
}
impl From<CorrelationId> for CausationId {
fn from(id: CorrelationId) -> Self {
Self(id.0)
}
}
#[derive(
Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
pub struct StreamId(Box<str>);
impl StreamId {
#[must_use]
pub fn new(id: impl Into<Box<str>>) -> Self {
let id: Box<str> = id.into();
assert!(!id.is_empty(), "StreamId must not be empty");
assert!(
!id.contains('\0'),
"StreamId must not contain NUL bytes, got: {id:?}"
);
Self(id)
}
pub fn try_new(id: impl Into<Box<str>>) -> Result<Self, crate::error::EngineError> {
let id: Box<str> = id.into();
if id.is_empty() {
return Err(crate::error::EngineError::InvalidStreamId {
input: id,
reason: "stream ID must not be empty",
});
}
if id.contains('\0') {
let truncated: Box<str> = id.chars().take(200).collect::<String>().into();
return Err(crate::error::EngineError::InvalidStreamId {
input: truncated,
reason: "stream ID must not contain NUL bytes",
});
}
Ok(Self(id))
}
#[must_use]
pub fn for_process(tenant_id: TenantId, process_id: &ProcessId) -> Self {
Self::new(format!("process/{tenant_id}/{process_id}"))
}
pub fn for_partner(partner_id: &str) -> Result<Self, crate::error::EngineError> {
if partner_id.contains('\0') || partner_id.contains('/') {
return Err(crate::error::EngineError::InvalidStreamId {
input: partner_id.chars().take(200).collect::<String>().into(),
reason: "partner_id must not contain '/' or NUL bytes",
});
}
Ok(Self::new(format!("partner/{partner_id}")))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for StreamId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl TryFrom<&str> for StreamId {
type Error = crate::error::EngineError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
Self::try_new(s)
}
}
impl TryFrom<String> for StreamId {
type Error = crate::error::EngineError;
fn try_from(s: String) -> Result<Self, Self::Error> {
Self::try_new(s)
}
}
impl TryFrom<Box<str>> for StreamId {
type Error = crate::error::EngineError;
fn try_from(s: Box<str>) -> Result<Self, Self::Error> {
Self::try_new(s)
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ProcessIdentity {
stream_id: StreamId,
pub process_id: ProcessId,
pub tenant_id: TenantId,
pub workflow_id: crate::version::WorkflowId,
}
impl ProcessIdentity {
#[must_use]
pub fn new(
process_id: ProcessId,
tenant_id: TenantId,
workflow_id: crate::version::WorkflowId,
) -> Self {
Self {
stream_id: StreamId::for_process(tenant_id, &process_id),
process_id,
tenant_id,
workflow_id,
}
}
#[must_use]
pub fn stream_id(&self) -> &StreamId {
&self.stream_id
}
}