use chrono::{DateTime, Utc};
use crate::causal::{CausalContext, MessageId};
use crate::channel::SchemaId;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PublisherId(String);
impl PublisherId {
#[must_use]
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for PublisherId {
fn from(value: &str) -> Self {
Self::new(value)
}
}
impl From<String> for PublisherId {
fn from(value: String) -> Self {
Self::new(value)
}
}
impl Default for PublisherId {
fn default() -> Self {
Self::new("anonymous")
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Envelope {
pub message_id: MessageId,
pub payload: Vec<u8>,
pub causal_context: Option<CausalContext>,
pub schema_id: SchemaId,
pub publisher_id: PublisherId,
pub timestamp: DateTime<Utc>,
}
impl Envelope {
#[must_use]
pub fn new(
payload: Vec<u8>,
causal_context: Option<CausalContext>,
schema_id: SchemaId,
publisher_id: PublisherId,
) -> Self {
Self::with_timestamp(payload, causal_context, schema_id, publisher_id, Utc::now())
}
#[must_use]
pub fn with_timestamp(
payload: Vec<u8>,
causal_context: Option<CausalContext>,
schema_id: SchemaId,
publisher_id: PublisherId,
timestamp: DateTime<Utc>,
) -> Self {
Self::with_message_id_and_timestamp(
MessageId::new(),
payload,
causal_context,
schema_id,
publisher_id,
timestamp,
)
}
#[must_use]
pub fn with_message_id(
message_id: MessageId,
payload: Vec<u8>,
causal_context: Option<CausalContext>,
schema_id: SchemaId,
publisher_id: PublisherId,
) -> Self {
Self::with_message_id_and_timestamp(
message_id,
payload,
causal_context,
schema_id,
publisher_id,
Utc::now(),
)
}
#[must_use]
pub const fn with_message_id_and_timestamp(
message_id: MessageId,
payload: Vec<u8>,
causal_context: Option<CausalContext>,
schema_id: SchemaId,
publisher_id: PublisherId,
timestamp: DateTime<Utc>,
) -> Self {
Self {
message_id,
payload,
causal_context,
schema_id,
publisher_id,
timestamp,
}
}
}
#[cfg(test)]
mod tests {
use chrono::Utc;
use super::{Envelope, PublisherId};
use crate::causal::{CausalContext, MessageId};
use crate::channel::SchemaId;
#[test]
fn envelope_carries_required_fields() {
let schema_id = SchemaId::new();
let publisher_id = PublisherId::from("publisher-1");
let parent = MessageId::new();
let causal_context = Some(CausalContext::child_of(parent));
let timestamp = fixed_timestamp();
let message_id = MessageId::new();
let envelope = Envelope::with_message_id_and_timestamp(
message_id,
b"{}".to_vec(),
causal_context.clone(),
schema_id,
publisher_id.clone(),
timestamp,
);
assert_eq!(envelope.message_id, message_id);
assert_eq!(envelope.payload, b"{}".to_vec());
assert_eq!(envelope.causal_context, causal_context);
assert_eq!(envelope.schema_id, schema_id);
assert_eq!(envelope.publisher_id, publisher_id);
assert_eq!(envelope.timestamp, timestamp);
}
#[test]
fn envelope_assigns_unique_message_ids() {
let first = Envelope::new(vec![], None, SchemaId::new(), PublisherId::default());
let second = Envelope::new(vec![], None, SchemaId::new(), PublisherId::default());
assert_ne!(first.message_id, second.message_id);
}
fn fixed_timestamp() -> chrono::DateTime<Utc> {
Utc::now()
}
}