pub mod orderer;
pub use orderer::{CausalOrderer, OrderedEnvelope};
use uuid::Uuid;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct MessageId(Uuid);
impl MessageId {
#[must_use]
pub fn new() -> Self {
Self(Uuid::new_v4())
}
#[must_use]
pub const fn from_uuid(uuid: Uuid) -> Self {
Self(uuid)
}
#[must_use]
pub const fn as_uuid(self) -> Uuid {
self.0
}
}
impl Default for MessageId {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CausalContext {
pub parent: Option<MessageId>,
pub parent_chain: Vec<MessageId>,
}
impl CausalContext {
#[must_use]
pub const fn root() -> Self {
Self {
parent: None,
parent_chain: Vec::new(),
}
}
#[must_use]
pub fn child_of(parent: MessageId) -> Self {
Self::from_parent_chain(vec![parent])
}
#[must_use]
pub fn child_of_context(parent: MessageId, parent_context: &Self) -> Self {
let mut parent_chain = Vec::with_capacity(parent_context.parent_chain.len() + 1);
parent_chain.push(parent);
parent_chain.extend_from_slice(&parent_context.parent_chain);
Self::from_parent_chain(parent_chain)
}
#[must_use]
pub fn from_parent_chain(parent_chain: Vec<MessageId>) -> Self {
let parent = parent_chain.first().copied();
Self {
parent,
parent_chain,
}
}
#[must_use]
pub fn parent_chain(&self) -> &[MessageId] {
&self.parent_chain
}
#[must_use]
pub fn happened_before(&self, ancestor_id: MessageId) -> bool {
self.parent_chain.contains(&ancestor_id)
}
}
impl Default for CausalContext {
fn default() -> Self {
Self::root()
}
}
#[cfg(test)]
mod tests {
use super::{CausalContext, MessageId};
#[test]
fn root_context_has_no_parent() {
assert_eq!(CausalContext::root().parent, None);
assert!(CausalContext::root().parent_chain().is_empty());
}
#[test]
fn child_context_carries_parent_reference() {
let parent = MessageId::new();
let context = CausalContext::child_of(parent);
assert_eq!(context.parent, Some(parent));
assert_eq!(context.parent_chain(), &[parent]);
}
#[test]
fn generated_message_ids_are_unique() {
let first = MessageId::new();
let second = MessageId::new();
assert_ne!(first, second);
}
#[test]
fn happened_before_follows_full_parent_chain() {
let a_id = MessageId::new();
let a_context = CausalContext::root();
let b_id = MessageId::new();
let b_context = CausalContext::child_of(a_id);
let c_id = MessageId::new();
let c_context = CausalContext::child_of_context(b_id, &b_context);
let d_context = CausalContext::root();
assert!(c_context.happened_before(a_id));
assert!(c_context.happened_before(b_id));
assert_eq!(c_context.parent_chain(), &[b_id, a_id]);
assert!(!a_context.happened_before(c_id));
assert!(!d_context.happened_before(a_id));
}
}