mod vector_clock; use uuid::Uuid; use crate::vector_clock::VectorClock; #[derive(Default)] pub struct Doc { ops: Vec, } impl Doc { pub fn append_op(&mut self, actor_id: &Uuid, payload: OpPayload) { // Increment the last clock for the provided actor let clock = self .ops .last() .map(|Op { clock, .. }| clock.inc(actor_id)) // For an empty document, initialize a new clock .unwrap_or_else(|| VectorClock::default().inc(actor_id)); self.ops.push(Op { id: Uuid::now_v7(), clock, payload, }); } pub fn realize(&self) -> RealizedDoc<'_> { let mut realized = RealizedDoc::default(); for op in &self.ops { op.apply(&mut realized); } realized } } pub struct Op { id: Uuid, payload: OpPayload, clock: VectorClock, } impl Op { fn apply<'a>(&'a self, realized: &mut RealizedDoc<'a>) { match &self.payload { OpPayload::CreateGrid { id: grid_id, rows, base_cells_per_row, } => { let rows = (0..*rows) .map(|row_idx| { let cells = (0..*base_cells_per_row) .map(|cell_idx| Cell { id: DerivedId::new(grid_id, "cell", cell_idx), }) .collect(); Row { id: DerivedId::new(grid_id, "row", row_idx), cells, } }) .collect(); realized.grids.push(Grid { id: grid_id, rows }); } } } } pub enum OpPayload { CreateGrid { id: Uuid, rows: usize, base_cells_per_row: usize, }, } #[derive(Default)] pub struct RealizedDoc<'a> { grids: Vec>, } pub struct Grid<'a> { id: &'a Uuid, rows: Vec>, } pub struct Row<'a> { id: DerivedId<'a>, cells: Vec>, } pub struct Cell<'a> { id: DerivedId<'a>, } pub struct DerivedId<'a> { id: &'a Uuid, tag: &'static str, index: usize, } impl<'a> DerivedId<'a> { pub fn new(id: &'a Uuid, tag: &'static str, index: usize) -> Self { Self { id, tag, index } } } #[cfg(test)] mod tests { use super::*; #[test] fn realize_doc() { let actor_id = Uuid::now_v7(); let mut doc = Doc::default(); doc.append_op( &actor_id, OpPayload::CreateGrid { id: Uuid::now_v7(), rows: 4, base_cells_per_row: 16, }, ); let realized = doc.realize(); assert!(realized.grids.len() == 1); let grid = realized.grids.first().unwrap(); assert!(grid.rows.len() == 4); let row = grid.rows.first().unwrap(); assert!(row.cells.len() == 16); } }