spec_ai_knowledge_graph/
vector_clock.rs

1use anyhow::{Context, Result};
2use serde::{Deserialize, Serialize};
3use std::collections::{HashMap, HashSet};
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
6pub struct VectorClock {
7    versions: HashMap<String, i64>,
8}
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ClockOrder {
12    Before,
13    After,
14    Concurrent,
15    Equal,
16}
17
18impl VectorClock {
19    pub fn new() -> Self {
20        Self {
21            versions: HashMap::new(),
22        }
23    }
24
25    pub fn from_json(json: &str) -> Result<Self> {
26        if json.is_empty() || json == "{}" {
27            return Ok(Self::new());
28        }
29        serde_json::from_str(json).context("parsing vector clock JSON")
30    }
31
32    pub fn to_json(&self) -> Result<String> {
33        serde_json::to_string(&self).context("serializing vector clock")
34    }
35
36    pub fn get(&self, instance_id: &str) -> i64 {
37        self.versions.get(instance_id).copied().unwrap_or(0)
38    }
39
40    pub fn set(&mut self, instance_id: String, version: i64) {
41        self.versions.insert(instance_id, version);
42    }
43
44    pub fn increment(&mut self, instance_id: &str) -> i64 {
45        let version = self.get(instance_id) + 1;
46        self.versions.insert(instance_id.to_string(), version);
47        version
48    }
49
50    pub fn merge(&mut self, other: &VectorClock) {
51        for (instance_id, &other_version) in &other.versions {
52            let current_version = self.get(instance_id);
53            if other_version > current_version {
54                self.versions.insert(instance_id.clone(), other_version);
55            }
56        }
57    }
58
59    pub fn compare(&self, other: &VectorClock) -> ClockOrder {
60        let mut self_less_or_equal = true;
61        let mut other_less_or_equal = true;
62
63        let all_instances: HashSet<_> = self.versions.keys().chain(other.versions.keys()).collect();
64
65        for instance_id in all_instances {
66            let self_version = self.get(instance_id);
67            let other_version = other.get(instance_id);
68
69            if self_version > other_version {
70                self_less_or_equal = false;
71            }
72            if other_version > self_version {
73                other_less_or_equal = false;
74            }
75        }
76
77        match (self_less_or_equal, other_less_or_equal) {
78            (true, true) => ClockOrder::Equal,
79            (true, false) => ClockOrder::Before,
80            (false, true) => ClockOrder::After,
81            (false, false) => ClockOrder::Concurrent,
82        }
83    }
84
85    pub fn happens_before(&self, other: &VectorClock) -> bool {
86        matches!(self.compare(other), ClockOrder::Before)
87    }
88
89    pub fn is_concurrent(&self, other: &VectorClock) -> bool {
90        matches!(self.compare(other), ClockOrder::Concurrent)
91    }
92
93    pub fn is_equal(&self, other: &VectorClock) -> bool {
94        matches!(self.compare(other), ClockOrder::Equal)
95    }
96
97    pub fn instances(&self) -> Vec<String> {
98        self.versions.keys().cloned().collect()
99    }
100
101    pub fn instance_count(&self) -> usize {
102        self.versions.len()
103    }
104
105    pub fn is_empty(&self) -> bool {
106        self.versions.is_empty()
107    }
108
109    pub fn clear(&mut self) {
110        self.versions.clear();
111    }
112}
113
114impl Default for VectorClock {
115    fn default() -> Self {
116        Self::new()
117    }
118}
119
120impl std::fmt::Display for VectorClock {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        write!(f, "{{")?;
123        let mut first = true;
124        for (instance_id, version) in &self.versions {
125            if !first {
126                write!(f, ", ")?;
127            }
128            write!(f, "{}: {}", instance_id, version)?;
129            first = false;
130        }
131        write!(f, "}}")
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn test_new_clock_is_empty() {
141        let clock = VectorClock::new();
142        assert!(clock.is_empty());
143        assert_eq!(clock.get("instance1"), 0);
144    }
145
146    #[test]
147    fn test_increment() {
148        let mut clock = VectorClock::new();
149        assert_eq!(clock.increment("instance1"), 1);
150        assert_eq!(clock.increment("instance1"), 2);
151        assert_eq!(clock.get("instance1"), 2);
152    }
153
154    #[test]
155    fn test_merge() {
156        let mut clock1 = VectorClock::new();
157        clock1.set("a".to_string(), 1);
158        clock1.set("b".to_string(), 2);
159
160        let mut clock2 = VectorClock::new();
161        clock2.set("b".to_string(), 3);
162        clock2.set("c".to_string(), 1);
163
164        clock1.merge(&clock2);
165
166        assert_eq!(clock1.get("a"), 1);
167        assert_eq!(clock1.get("b"), 3);
168        assert_eq!(clock1.get("c"), 1);
169    }
170}