From 2418583618ece59fd28b5b04bedffccdae11a170 Mon Sep 17 00:00:00 2001 From: Josh Kingsley Date: Tue, 18 Nov 2025 22:40:25 +0200 Subject: feat(crdt): keep deleted cells --- crdt/src/lib.rs | 204 ++++++++------------------------------------------------ 1 file changed, 29 insertions(+), 175 deletions(-) (limited to 'crdt/src/lib.rs') diff --git a/crdt/src/lib.rs b/crdt/src/lib.rs index cd86b97..50ccd20 100644 --- a/crdt/src/lib.rs +++ b/crdt/src/lib.rs @@ -177,7 +177,7 @@ impl Op { let duration: Ratio = span_duration / *subdivisions as u32; row.value_mut().cells.splice( - i..(j + 1), + i..i, (0..*subdivisions).map(|subdivision_idx| { Entry::active(Cell { id: self.id.derive_id("cell", subdivision_idx), @@ -232,6 +232,10 @@ impl Entry { Self::Active { value } } + pub fn is_active(&self) -> bool { + matches!(self, Self::Active { .. }) + } + pub fn is_active_and(&self, f: impl FnOnce(&T) -> bool) -> bool { match self { Self::Active { value } => f(value), @@ -317,203 +321,53 @@ mod tests { use super::*; #[test] - fn merge() { - let actor1 = Uuid::now_v7(); - let actor2 = Uuid::now_v7(); - - let mut doc1 = State::from_ops( - &actor1, - &[OpData::CreateGrid { - rows: 4, - base_cells_per_row: 16, - }], - ); - - let mut doc2 = State::from_ops( - &actor2, - &[OpData::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 mut doc1 = State::from_ops( - &actor1, - &[OpData::CreateGrid { - rows: 4, - base_cells_per_row: 16, - }], - ); - - let mut doc2 = doc1.clone(); - - { - let realized = doc1.realize().unwrap(); - - doc1.append_op( - &actor1, - OpData::ChangeSubdivisions { - grid_id: realized.grids[0].value().id, - row_id: realized.grids[0].value().rows[0].value().id, - start_cell_id: realized.grids[0].value().rows[0].value().cells[0] - .value() - .id, - end_cell_id: realized.grids[0].value().rows[0].value().cells[3] - .value() - .id, - subdivisions: 3, - }, - ); - - doc2.append_op( - &actor2, - OpData::ChangeSubdivisions { - grid_id: realized.grids[0].value().id, - row_id: realized.grids[0].value().rows[0].value().id, - start_cell_id: realized.grids[0].value().rows[0].value().cells[0] - .value() - .id, - end_cell_id: realized.grids[0].value().rows[0].value().cells[3] - .value() - .id, - subdivisions: 3, - }, - ); - } - - assert_eq!( - doc1.ops - .last() - .unwrap() - .clock - .partial_cmp(&doc2.ops.last().unwrap().clock), - None - ); - - doc1.merge(&doc2); - - assert_eq!(doc1.ops.len(), 3); - - let realized = doc1.realize().unwrap(); - - let grid = &realized.grids[0].value(); - let row = &grid.rows[0].value(); - - assert_eq!( - row.cells - .iter() - .map(|cell| cell.value().duration) - .sum::>(), - Ratio::ONE - ); - } - - #[test] - fn realize_doc() { + fn test() { let actor_id = Uuid::now_v7(); - let mut doc = State::default(); - - doc.append_op( + let mut state = State::from_ops( &actor_id, - OpData::CreateGrid { + &vec![OpData::CreateGrid { rows: 4, base_cells_per_row: 16, - }, + }], ); { - let realized = doc.realize().unwrap(); - - assert_eq!(realized.grids.len(), 1); - - let grid = realized.grids.first().unwrap().value(); + let doc = state.realize().unwrap(); + let grid = doc.grids[0].value(); + let row = grid.rows[0].value(); + assert_eq!(doc.grids.len(), 1); assert_eq!(grid.rows.len(), 4); - - let row = grid.rows.first().unwrap().value(); - assert_eq!(row.cells.len(), 16); - assert_eq!( - row.cells - .iter() - .map(|cell| cell.value().duration) - .sum::>(), - Ratio::ONE - ); - - doc.append_op( + state.append_op( &actor_id, OpData::ChangeSubdivisions { - grid_id: grid.id.clone(), - row_id: row.id.clone(), - start_cell_id: row.cells[0].value().id.clone(), - end_cell_id: row.cells[3].value().id.clone(), + grid_id: grid.id, + row_id: row.id, + start_cell_id: row.cells[0].value().id, + end_cell_id: row.cells[3].value().id, subdivisions: 3, }, ); } { - let realized = doc.realize().unwrap(); - - assert_eq!(realized.grids[0].value().rows[0].value().cells.len(), 15); - assert_eq!(realized.grids[0].value().rows[1].value().cells.len(), 16); - - let grid = &realized.grids[0].value(); - let row = &grid.rows[0].value(); - - assert_eq!( - row.cells - .iter() - .map(|cell| cell.value().duration) - .sum::>(), - Ratio::ONE - ); - - doc.append_op( - &actor_id, - OpData::ChangeSubdivisions { - grid_id: grid.id.clone(), - row_id: row.id.clone(), - start_cell_id: row.cells[0].value().id.clone(), - end_cell_id: row.cells.last().unwrap().value().id.clone(), - subdivisions: 12, - }, - ); - } - - { - let realized = doc.realize().unwrap(); - - let grid = &realized.grids[0].value(); - let row = &grid.rows[0].value(); + let doc = state.realize().unwrap(); + let grid = doc.grids[0].value(); + let row = grid.rows[0].value(); - assert_eq!(row.cells.len(), 12); - assert_eq!(realized.grids[0].value().rows[1].value().cells.len(), 16); + assert_eq!(doc.grids.len(), 1); + assert_eq!(grid.rows.len(), 4); + assert_eq!(row.cells.len(), 19); assert_eq!( - row.cells - .iter() - .map(|cell| cell.value().duration) - .sum::>(), - Ratio::ONE + vec![ + true, true, true, false, false, false, false, true, true, true, true, true, + true, true, true, true, true, true, true + ], + row.cells.iter().map(|e| e.is_active()).collect::>() ); } } -- cgit v1.2.3