summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Kingsley <josh@joshkingsley.me>2025-11-23 17:27:44 +0200
committerJosh Kingsley <josh@joshkingsley.me>2025-11-23 17:27:44 +0200
commitc2a6efb1b761014a90d90373cad47a14054af40b (patch)
tree0c1127b0fea0456409502ec9a3105fe18a8a6434
parenteb96b8eba0faf07adc5be4463759ec8b64ddc0e0 (diff)
feat(crdt): add wasm-bindgen
-rw-r--r--Cargo.lock44
-rw-r--r--crdt/Cargo.toml6
-rw-r--r--crdt/src/doc.rs15
-rw-r--r--crdt/src/lib.rs58
4 files changed, 103 insertions, 20 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 4156a98..71ed3e6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -53,8 +53,11 @@ name = "notive-crdt"
version = "0.1.0"
dependencies = [
"num-rational",
+ "serde",
+ "serde-wasm-bindgen",
"thiserror",
"uuid",
+ "wasm-bindgen",
]
[[package]]
@@ -133,6 +136,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde-wasm-bindgen"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
+dependencies = [
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "syn"
version = "2.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/crdt/Cargo.toml b/crdt/Cargo.toml
index fe50142..71f745a 100644
--- a/crdt/Cargo.toml
+++ b/crdt/Cargo.toml
@@ -3,7 +3,13 @@ name = "notive-crdt"
version = "0.1.0"
edition = "2024"
+[lib]
+crate-type = ["cdylib"]
+
[dependencies]
num-rational = "0.4.2"
+serde = { version = "1.0.228", features = ["derive"] }
+serde-wasm-bindgen = "0.6.5"
thiserror = "2.0.17"
uuid = { version = "1.18.1", features = ["v7"] }
+wasm-bindgen = "0.2.105"
diff --git a/crdt/src/doc.rs b/crdt/src/doc.rs
index bbd24d0..fcca1d8 100644
--- a/crdt/src/doc.rs
+++ b/crdt/src/doc.rs
@@ -1,3 +1,4 @@
+use serde::{Deserialize, Serialize};
use thiserror::Error;
use uuid::Uuid;
@@ -41,21 +42,33 @@ impl DerivableId for DerivedId {
}
}
-#[derive(Default)]
+impl Serialize for DerivedId {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ serializer.serialize_str(&self.to_string())
+ }
+}
+
+#[derive(Default, Serialize)]
pub struct Doc {
pub(crate) grids: Vec<Grid>,
}
+#[derive(Serialize)]
pub struct Grid {
pub(crate) id: DerivedId,
pub(crate) rows: Vec<Row>,
}
+#[derive(Serialize)]
pub struct Row {
pub(crate) id: DerivedId,
pub(crate) cells: Vec<Cell>,
}
+#[derive(Serialize)]
pub struct Cell {
pub(crate) id: DerivedId,
}
diff --git a/crdt/src/lib.rs b/crdt/src/lib.rs
index 977763f..3c89ce9 100644
--- a/crdt/src/lib.rs
+++ b/crdt/src/lib.rs
@@ -1,13 +1,10 @@
-//! This crate defines the distributed state management system for
-//! Notive. The state is an operation-based CRDT, which given the same
-//! set of operations on any client, will realize the same state.
-
use thiserror::Error;
use uuid::Uuid;
+use wasm_bindgen::prelude::*;
use crate::{
doc::{ApplyOpError, Doc},
- op::{Op, OpKind},
+ op::{CreateGrid, Op, OpKind},
vector_clock::VectorClock,
};
@@ -21,18 +18,28 @@ pub enum Error {
RealizeError(#[from] ApplyOpError),
}
-#[derive(Default)]
+#[wasm_bindgen]
pub struct State {
+ actor_id: Uuid,
ops: Vec<Op>,
}
impl State {
- pub fn append_op(&mut self, actor_id: &Uuid, kind: OpKind) {
+ pub fn new() -> Self {
+ let actor_id = Uuid::now_v7();
+
+ Self {
+ actor_id,
+ ops: vec![],
+ }
+ }
+
+ pub fn append_op(&mut self, kind: OpKind) {
let clock = self
.ops
.last()
- .map(|op| op.clock.inc(actor_id))
- .unwrap_or_else(|| VectorClock::new().inc(actor_id));
+ .map(|op| op.clock.inc(&self.actor_id))
+ .unwrap_or_else(|| VectorClock::new().inc(&self.actor_id));
self.ops.push(Op {
id: Uuid::now_v7(),
@@ -52,6 +59,24 @@ impl State {
}
}
+#[wasm_bindgen]
+pub fn make_state() -> State {
+ State::new()
+}
+
+#[wasm_bindgen]
+pub fn create_grid(state: &mut State) {
+ state.append_op(OpKind::CreateGrid(CreateGrid {
+ rows: 4,
+ base_cells_per_row: 16
+ }));
+}
+
+pub fn realize(state: &State) -> JsValue {
+ let doc = state.realize().unwrap();
+ serde_wasm_bindgen::to_value(&doc).unwrap()
+}
+
#[cfg(test)]
mod tests {
use crate::op::CreateGrid;
@@ -60,17 +85,12 @@ mod tests {
#[test]
fn test() {
- let alice = Uuid::now_v7();
-
- let mut state = State::default();
+ let mut state = State::new();
- state.append_op(
- &alice,
- OpKind::CreateGrid(CreateGrid {
- rows: 4,
- base_cells_per_row: 16,
- }),
- );
+ state.append_op(OpKind::CreateGrid(CreateGrid {
+ rows: 4,
+ base_cells_per_row: 16,
+ }));
let doc = state.realize().unwrap();
let grid = doc.grids.first().unwrap();