diff options
Diffstat (limited to 'crdt/src')
| -rw-r--r-- | crdt/src/lib.rs | 106 |
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); } } |
