Skip to main content

entrenar/integrity/lineage/
timestamp.rs

1//! Lamport logical timestamp implementation
2
3use serde::{Deserialize, Serialize};
4use std::cmp::Ordering;
5
6/// Lamport logical timestamp for causal ordering
7///
8/// Implements Lamport's logical clock algorithm for establishing
9/// happens-before relationships without synchronized wall clocks.
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
11pub struct LamportTimestamp {
12    /// Logical counter value
13    pub counter: u64,
14    /// Node identifier for tie-breaking
15    pub node_id: String,
16}
17
18impl LamportTimestamp {
19    /// Create a new timestamp for a node
20    pub fn new(node_id: &str) -> Self {
21        Self { counter: 0, node_id: node_id.to_string() }
22    }
23
24    /// Create a timestamp with specific counter value
25    pub fn with_counter(node_id: &str, counter: u64) -> Self {
26        Self { counter, node_id: node_id.to_string() }
27    }
28
29    /// Increment the timestamp for a local event
30    ///
31    /// Returns a copy of the new timestamp value.
32    pub fn increment(&mut self) -> Self {
33        self.counter += 1;
34        self.clone()
35    }
36
37    /// Merge with another timestamp (on message receive)
38    ///
39    /// Sets counter to max(self.counter, other.counter) + 1
40    /// Returns a copy of the new timestamp value.
41    pub fn merge(&mut self, other: &Self) -> Self {
42        self.counter = self.counter.max(other.counter) + 1;
43        self.clone()
44    }
45
46    /// Check if this timestamp happens-before another
47    ///
48    /// Returns true if:
49    /// - self.counter < other.counter, OR
50    /// - self.counter == other.counter AND self.node_id < other.node_id
51    ///
52    /// Note: If neither happens_before the other, events are concurrent.
53    pub fn happens_before(&self, other: &Self) -> bool {
54        match self.counter.cmp(&other.counter) {
55            Ordering::Less => true,
56            Ordering::Greater => false,
57            Ordering::Equal => self.node_id < other.node_id,
58        }
59    }
60
61    /// Check if events are concurrent (neither happens-before the other)
62    pub fn is_concurrent_with(&self, other: &Self) -> bool {
63        self.counter == other.counter && self.node_id != other.node_id
64    }
65}
66
67impl PartialOrd for LamportTimestamp {
68    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
69        Some(self.cmp(other))
70    }
71}
72
73impl Ord for LamportTimestamp {
74    fn cmp(&self, other: &Self) -> Ordering {
75        match self.counter.cmp(&other.counter) {
76            Ordering::Equal => self.node_id.cmp(&other.node_id),
77            other => other,
78        }
79    }
80}