summaryrefslogtreecommitdiff
path: root/crdt
diff options
context:
space:
mode:
Diffstat (limited to 'crdt')
-rw-r--r--crdt/src/lib.rs85
1 files changed, 81 insertions, 4 deletions
diff --git a/crdt/src/lib.rs b/crdt/src/lib.rs
index 39c5f0e..3983692 100644
--- a/crdt/src/lib.rs
+++ b/crdt/src/lib.rs
@@ -1,6 +1,6 @@
mod vector_clock;
-use std::fmt::Display;
+use std::{collections::BTreeSet, fmt::Display};
use uuid::Uuid;
@@ -14,12 +14,32 @@ pub enum Error {
NotFound(DerivedId),
}
-#[derive(Default)]
+#[derive(Default, Clone)]
pub struct Doc {
ops: Vec<Op>,
}
impl Doc {
+ pub fn from_ops(actor_id: &Uuid, payloads: &[OpPayload]) -> Self {
+ let mut clock = VectorClock::new();
+
+ let ops = payloads
+ .iter()
+ .cloned()
+ .map(|payload| {
+ clock = clock.inc(&actor_id);
+
+ Op {
+ id: Uuid::now_v7(),
+ clock: clock.clone(),
+ payload,
+ }
+ })
+ .collect();
+
+ Doc { ops }
+ }
+
pub fn append_op(&mut self, actor_id: &Uuid, payload: OpPayload) {
// Increment the last clock for the provided actor
let clock = self
@@ -36,6 +56,18 @@ impl Doc {
});
}
+ pub fn merge(&mut self, other: &Doc) {
+ let op_ids: BTreeSet<Uuid> = self.ops.iter().map(|op| op.id).collect();
+
+ for op in &other.ops {
+ if !op_ids.contains(&op.id) {
+ self.ops.push(op.clone());
+ }
+ }
+
+ self.ops.sort_by(|op1, op2| op1.clock.cmp(&op2.clock));
+ }
+
pub fn realize(&self) -> Result<RealizedDoc, Error> {
let mut realized = RealizedDoc::default();
@@ -47,7 +79,7 @@ impl Doc {
}
}
-#[derive(Debug)]
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Op {
id: Uuid,
payload: OpPayload,
@@ -132,7 +164,7 @@ impl Op {
}
}
-#[derive(Debug)]
+#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OpPayload {
CreateGrid {
rows: usize,
@@ -203,6 +235,51 @@ mod tests {
use super::*;
#[test]
+ fn merge() {
+ let actor1 = Uuid::now_v7();
+ let actor2 = Uuid::now_v7();
+
+ let mut doc1 = Doc::from_ops(
+ &actor1,
+ &[OpPayload::CreateGrid {
+ rows: 4,
+ base_cells_per_row: 16,
+ }],
+ );
+
+ let mut doc2 = Doc::from_ops(
+ &actor2,
+ &[OpPayload::CreateGrid {
+ rows: 4,
+ base_cells_per_row: 16,
+ }],
+ );
+
+ doc1.merge(&doc2);
+
+ assert_eq!(doc1.ops.len(), 2);
+ assert_eq!(doc1.ops.last().unwrap(), doc2.ops.last().unwrap());
+
+ doc2.merge(&doc1);
+
+ assert_eq!(doc2.ops.len(), 2);
+ }
+
+ #[test]
+ fn concurrent_ops() {
+ let actor1 = Uuid::now_v7();
+ let actor2 = Uuid::now_v7();
+
+ let doc = Doc::from_ops(
+ &actor1,
+ &[OpPayload::CreateGrid {
+ rows: 4,
+ base_cells_per_row: 16,
+ }],
+ );
+ }
+
+ #[test]
fn realize_doc() {
let actor_id = Uuid::now_v7();