summaryrefslogtreecommitdiff
path: root/crdt/src
diff options
context:
space:
mode:
Diffstat (limited to 'crdt/src')
-rw-r--r--crdt/src/lib.rs106
1 files changed, 100 insertions, 6 deletions
diff --git a/crdt/src/lib.rs b/crdt/src/lib.rs
index 4c41dd0..c0214b3 100644
--- a/crdt/src/lib.rs
+++ b/crdt/src/lib.rs
@@ -1,9 +1,19 @@
mod vector_clock;
+use std::fmt::Display;
+
use uuid::Uuid;
use crate::vector_clock::VectorClock;
+use thiserror::Error;
+
+#[derive(Error, Debug)]
+pub enum Error {
+ #[error("object with ID {0} not found")]
+ NotFound(DerivedId),
+}
+
#[derive(Default)]
pub struct Doc {
ops: Vec<Op>,
@@ -26,17 +36,18 @@ impl Doc {
});
}
- pub fn realize(&self) -> RealizedDoc {
+ pub fn realize(&self) -> Result<RealizedDoc, Error> {
let mut realized = RealizedDoc::default();
for op in &self.ops {
- op.apply(&mut realized);
+ op.apply(&mut realized)?;
}
- realized
+ Ok(realized)
}
}
+#[derive(Debug)]
pub struct Op {
id: Uuid,
payload: OpPayload,
@@ -44,7 +55,7 @@ pub struct Op {
}
impl Op {
- fn apply(&self, realized: &mut RealizedDoc) {
+ fn apply(&self, realized: &mut RealizedDoc) -> Result<(), Error> {
match &self.payload {
OpPayload::CreateGrid {
rows,
@@ -70,42 +81,109 @@ impl Op {
rows,
});
}
+
+ OpPayload::ChangeSubdivisions {
+ grid_id,
+ row_id,
+ start_cell_id,
+ end_cell_id,
+ subdivisions,
+ } => {
+ let grid = realized
+ .grids
+ .iter_mut()
+ .find(|g| g.id == *grid_id)
+ .ok_or(Error::NotFound(grid_id.clone()))?;
+
+ let row = grid
+ .rows
+ .iter_mut()
+ .find(|r| r.id == *row_id)
+ .ok_or(Error::NotFound(row_id.clone()))?;
+
+ let start_cell_idx = row
+ .cells
+ .iter()
+ .position(|c| c.id == *start_cell_id)
+ .ok_or(Error::NotFound(start_cell_id.clone()))?;
+
+ let end_cell_idx = row
+ .cells
+ .iter()
+ .position(|c| c.id == *end_cell_id)
+ .ok_or(Error::NotFound(end_cell_id.clone()))?;
+
+ let (i, j) = if start_cell_idx <= end_cell_idx {
+ (start_cell_idx, end_cell_idx)
+ } else {
+ (end_cell_idx, start_cell_idx)
+ };
+
+ row.cells.splice(
+ i..(j + 1),
+ (0..*subdivisions).map(|subdivision_idx| Cell {
+ id: self.id.derive_id("cell", subdivision_idx),
+ }),
+ );
+ }
}
+
+ Ok(())
}
}
+#[derive(Debug)]
pub enum OpPayload {
CreateGrid {
rows: usize,
base_cells_per_row: usize,
},
+
+ ChangeSubdivisions {
+ grid_id: DerivedId,
+ row_id: DerivedId,
+ start_cell_id: DerivedId,
+ end_cell_id: DerivedId,
+ subdivisions: usize,
+ },
}
-#[derive(Default)]
+#[derive(Default, Debug)]
pub struct RealizedDoc {
grids: Vec<Grid>,
}
+#[derive(Debug)]
pub struct Grid {
id: DerivedId,
rows: Vec<Row>,
}
+#[derive(Debug)]
pub struct Row {
id: DerivedId,
cells: Vec<Cell>,
}
+#[derive(Debug)]
pub struct Cell {
id: DerivedId,
}
+#[derive(PartialEq, Eq, Debug, Clone)]
pub struct DerivedId {
+ // TODO These IDs can be interned on the Doc
id: Uuid,
tag: &'static str,
index: usize,
}
+impl Display for DerivedId {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}:{}:{}", self.id, self.tag, self.index)
+ }
+}
+
trait DerivableId {
fn derive_id(&self, tag: &'static str, index: usize) -> DerivedId;
}
@@ -138,7 +216,7 @@ mod tests {
},
);
- let realized = doc.realize();
+ let realized = doc.realize().unwrap();
assert!(realized.grids.len() == 1);
@@ -149,5 +227,21 @@ mod tests {
let row = grid.rows.first().unwrap();
assert!(row.cells.len() == 16);
+
+ doc.append_op(
+ &actor_id,
+ OpPayload::ChangeSubdivisions {
+ grid_id: grid.id.clone(),
+ row_id: row.id.clone(),
+ start_cell_id: row.cells[0].id.clone(),
+ end_cell_id: row.cells[3].id.clone(),
+ subdivisions: 3,
+ },
+ );
+
+ let realized2 = doc.realize().unwrap();
+
+ assert_eq!(realized2.grids[0].rows[0].cells.len(), 15);
+ assert_eq!(realized2.grids[0].rows[1].cells.len(), 16);
}
}