use serde::{Deserialize, Serialize}; use thiserror::Error; use tsify::Tsify; use uuid::Uuid; use crate::op::{ChangeSubdivisions, CreateGrid, Op, OpKind}; /// An deterministically derived ID, e.g. a grid ID derived from the /// op ID which creates it. #[derive(Tsify)] #[tsify(type = "string")] pub struct DerivedId { base: String, tag: String, index: usize, } impl ToString for DerivedId { fn to_string(&self) -> String { format!("{}:{}={}", self.base, self.tag, self.index) } } trait DerivableId { fn derive_id(&self, tag: &str, index: usize) -> DerivedId; } impl DerivableId for Uuid { fn derive_id(&self, tag: &str, index: usize) -> DerivedId { DerivedId { base: self.to_string(), tag: tag.to_string(), index, } } } impl DerivableId for DerivedId { fn derive_id(&self, tag: &str, index: usize) -> DerivedId { DerivedId { base: self.to_string(), tag: tag.to_string(), index, } } } impl Serialize for DerivedId { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serializer.serialize_str(&self.to_string()) } } impl<'de> Deserialize<'de> for DerivedId { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let s = String::deserialize(deserializer)?; let parts: Vec<&str> = s.split(':').collect(); if parts.len() != 2 { return Err(serde::de::Error::custom("Invalid DerivedId format")); } let base = parts[0].to_string(); let tag_index: Vec<&str> = parts[1].split('=').collect(); if tag_index.len() != 2 { return Err(serde::de::Error::custom("Invalid DerivedId format")); } let tag = tag_index[0].to_string(); let index = tag_index[1] .parse() .map_err(|_| serde::de::Error::custom("Invalid index"))?; Ok(DerivedId { base, tag, index }) } } #[derive(Default, Serialize, Deserialize, Tsify)] #[tsify(into_wasm_abi)] pub struct Doc { pub grids: Vec, } #[derive(Serialize, Deserialize, Tsify)] #[tsify(into_wasm_abi)] pub struct Grid { pub id: DerivedId, pub rows: Vec, } #[derive(Serialize, Deserialize, Tsify)] #[tsify(into_wasm_abi)] pub struct Row { pub id: DerivedId, pub cells: Vec, } #[derive(Serialize, Deserialize, Tsify)] #[tsify(into_wasm_abi)] pub struct Cell { pub id: DerivedId, } #[derive(Error, Debug)] pub enum ApplyOpError {} pub type ApplyOpResult = Result<(), ApplyOpError>; impl Doc { pub fn apply_op(&mut self, op: &Op) -> ApplyOpResult { match &op.kind { OpKind::CreateGrid(data) => apply_create_grid(self, &op.id, data), OpKind::ChangeSubdivisions(data) => apply_change_subdivisions(self, data), } } } fn apply_create_grid(doc: &mut Doc, op_id: &Uuid, data: &CreateGrid) -> ApplyOpResult { let grid_id = op_id.derive_id("grid", 0); let rows = (0..data.rows) .map(|row_idx| { let row_id = grid_id.derive_id("row", row_idx); let cells = (0..data.base_cells_per_row) .map(|cell_idx| Cell { id: row_id.derive_id("cell", cell_idx), }) .collect(); Row { id: row_id, cells } }) .collect(); doc.grids.push(Grid { id: grid_id, rows }); Ok(()) } fn apply_change_subdivisions(_doc: &mut Doc, _data: &ChangeSubdivisions) -> ApplyOpResult { todo!() }