fix: wasm interface

This commit is contained in:
leeeon233 2022-11-22 10:39:43 +08:00 committed by Leonzhao
parent 71fd00418e
commit e124bbbec1
13 changed files with 194 additions and 84 deletions

12
Cargo.lock generated
View file

@ -788,6 +788,7 @@ dependencies = [
"ring", "ring",
"rle", "rle",
"serde", "serde",
"serde-wasm-bindgen",
"serde_columnar", "serde_columnar",
"serde_json", "serde_json",
"smallvec", "smallvec",
@ -1483,6 +1484,17 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "serde-wasm-bindgen"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
dependencies = [
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]] [[package]]
name = "serde_columnar" name = "serde_columnar"
version = "0.1.0" version = "0.1.0"

View file

@ -26,6 +26,7 @@ tabled = { version = "0.10.0", optional = true }
colored = "2.0.0" colored = "2.0.0"
bit-vec = "0.6.3" bit-vec = "0.6.3"
wasm-bindgen = { version = "0.2.83", optional = true } wasm-bindgen = { version = "0.2.83", optional = true }
serde-wasm-bindgen = { version = "0.4.5", optional = true }
js-sys = { version = "0.3.60", optional = true } js-sys = { version = "0.3.60", optional = true }
serde_json = { version = "1.0.87", optional = true } serde_json = { version = "1.0.87", optional = true }
arref = "0.1.0" arref = "0.1.0"
@ -52,7 +53,7 @@ debug-log = "0.1.1"
doctest = false doctest = false
[features] [features]
wasm = ["wasm-bindgen", "js-sys"] wasm = ["wasm-bindgen", "js-sys", "serde-wasm-bindgen"]
json = ["serde_json"] json = ["serde_json"]
# whether to use list slice instead of raw str in text container # whether to use list slice instead of raw str in text container
test_utils = ["crdt-list/fuzzing", "rand", "arbitrary", "tabled", "json"] test_utils = ["crdt-list/fuzzing", "rand", "arbitrary", "tabled", "json"]

View file

@ -23,6 +23,7 @@ use crate::{
op::{InnerContent, Op, RemoteContent, RichOp}, op::{InnerContent, Op, RemoteContent, RichOp},
value::LoroValue, value::LoroValue,
version::IdSpanVector, version::IdSpanVector,
LoroError,
}; };
use super::list_op::InnerListOp; use super::list_op::InnerListOp;
@ -90,6 +91,13 @@ impl ListContainer {
Some(id) Some(id)
} }
pub fn get(&self, pos: usize) -> Option<LoroValue> {
self.state
.get(pos)
.map(|range| self.raw_data.slice(&range.as_ref().0))
.and_then(|slice| slice.first().cloned())
}
pub fn delete<C: Context>(&mut self, ctx: &C, pos: usize, len: usize) -> Option<ID> { pub fn delete<C: Context>(&mut self, ctx: &C, pos: usize, len: usize) -> Option<ID> {
if len == 0 { if len == 0 {
return None; return None;
@ -279,7 +287,12 @@ impl List {
} }
} }
pub fn insert_batch<C: Context>(&mut self, ctx: &C, pos: usize, values: Vec<LoroValue>) { pub fn insert_batch<C: Context>(
&mut self,
ctx: &C,
pos: usize,
values: Vec<LoroValue>,
) -> Result<(), LoroError> {
self.with_container_checked(ctx, |x| x.insert_batch(ctx, pos, values)) self.with_container_checked(ctx, |x| x.insert_batch(ctx, pos, values))
} }
@ -288,7 +301,7 @@ impl List {
ctx: &C, ctx: &C,
pos: usize, pos: usize,
value: V, value: V,
) -> Option<ID> { ) -> Result<Option<ID>, LoroError> {
self.with_container_checked(ctx, |x| x.insert(ctx, pos, value)) self.with_container_checked(ctx, |x| x.insert(ctx, pos, value))
} }
@ -297,12 +310,21 @@ impl List {
ctx: &C, ctx: &C,
pos: usize, pos: usize,
obj: ContainerType, obj: ContainerType,
) -> ContainerID { ) -> Result<ContainerID, LoroError> {
self.with_container_checked(ctx, |x| x.insert_obj(ctx, pos, obj)) self.with_container_checked(ctx, |x| x.insert_obj(ctx, pos, obj))
} }
pub fn delete<C: Context>(&mut self, ctx: &C, pos: usize, len: usize) -> Option<ID> { pub fn delete<C: Context>(
self.with_container_checked(ctx, |text| text.delete(ctx, pos, len)) &mut self,
ctx: &C,
pos: usize,
len: usize,
) -> Result<Option<ID>, LoroError> {
self.with_container_checked(ctx, |list| list.delete(ctx, pos, len))
}
pub fn get(&self, pos: usize) -> Option<LoroValue> {
self.with_container(|list| list.get(pos))
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {

View file

@ -117,6 +117,11 @@ impl MapContainer {
pub fn delete<C: Context>(&mut self, ctx: &C, key: InternalString) { pub fn delete<C: Context>(&mut self, ctx: &C, key: InternalString) {
self.insert(ctx, key, LoroValue::Null); self.insert(ctx, key, LoroValue::Null);
} }
#[inline]
pub fn get(&self, key: &InternalString) -> Option<LoroValue> {
self.state.get(key).map(|v| &v.value).cloned()
}
} }
impl Container for MapContainer { impl Container for MapContainer {
@ -230,7 +235,12 @@ impl Map {
} }
} }
pub fn insert<C: Context, V: Into<LoroValue>>(&mut self, ctx: &C, key: &str, value: V) { pub fn insert<C: Context, V: Into<LoroValue>>(
&mut self,
ctx: &C,
key: &str,
value: V,
) -> Result<(), crate::LoroError> {
self.with_container_checked(ctx, |map| { self.with_container_checked(ctx, |map| {
map.insert(ctx, key.into(), value); map.insert(ctx, key.into(), value);
}) })
@ -241,16 +251,20 @@ impl Map {
ctx: &C, ctx: &C,
key: &str, key: &str,
obj: ContainerType, obj: ContainerType,
) -> ContainerID { ) -> Result<ContainerID, crate::LoroError> {
self.with_container_checked(ctx, |map| map.insert_obj(ctx, key.into(), obj)) self.with_container_checked(ctx, |map| map.insert_obj(ctx, key.into(), obj))
} }
pub fn delete<C: Context>(&mut self, ctx: &C, key: &str) { pub fn delete<C: Context>(&mut self, ctx: &C, key: &str) -> Result<(), crate::LoroError> {
self.with_container_checked(ctx, |map| { self.with_container_checked(ctx, |map| {
map.delete(ctx, key.into()); map.delete(ctx, key.into());
}) })
} }
pub fn get(&self, key: &str) -> Option<LoroValue> {
self.with_container(|map| map.get(&key.into()))
}
pub fn id(&self) -> ContainerID { pub fn id(&self) -> ContainerID {
self.instance.lock().unwrap().as_map().unwrap().id.clone() self.instance.lock().unwrap().as_map().unwrap().id.clone()
} }

View file

@ -14,7 +14,7 @@ use crate::{
id::{ClientID, ContainerIdx}, id::{ClientID, ContainerIdx},
op::{RemoteContent, RichOp}, op::{RemoteContent, RichOp},
version::IdSpanVector, version::IdSpanVector,
LoroValue, VersionVector, LoroError, LoroValue, VersionVector,
}; };
use super::{ use super::{
@ -297,19 +297,18 @@ pub trait ContainerWrapper {
where where
F: FnOnce(&mut Self::Container) -> R; F: FnOnce(&mut Self::Container) -> R;
fn with_container_checked<C: Context, F, R>(&self, ctx: &C, f: F) -> R fn with_container_checked<C: Context, F, R>(&self, ctx: &C, f: F) -> Result<R, LoroError>
where where
F: FnOnce(&mut Self::Container) -> R, F: FnOnce(&mut Self::Container) -> R,
{ {
let store_client_id = ctx.log_store().read().unwrap().this_client_id(); let store_client_id = ctx.log_store().read().unwrap().this_client_id();
if store_client_id != self.client_id() { if store_client_id != self.client_id() {
panic!( return Err(LoroError::UnmatchedContext {
"Context's client_id({}) does not match Container's client_id({})", expected: self.client_id(),
store_client_id, found: store_client_id,
self.client_id() });
);
} }
self.with_container(f) Ok(self.with_container(f))
} }
fn client_id(&self) -> ClientID; fn client_id(&self) -> ClientID;

View file

@ -302,11 +302,21 @@ impl Text {
self.instance.lock().unwrap().as_text().unwrap().id.clone() self.instance.lock().unwrap().as_text().unwrap().id.clone()
} }
pub fn insert<C: Context>(&mut self, ctx: &C, pos: usize, text: &str) -> Option<ID> { pub fn insert<C: Context>(
&mut self,
ctx: &C,
pos: usize,
text: &str,
) -> Result<Option<ID>, crate::LoroError> {
self.with_container_checked(ctx, |x| x.insert(ctx, pos, text)) self.with_container_checked(ctx, |x| x.insert(ctx, pos, text))
} }
pub fn delete<C: Context>(&mut self, ctx: &C, pos: usize, len: usize) -> Option<ID> { pub fn delete<C: Context>(
&mut self,
ctx: &C,
pos: usize,
len: usize,
) -> Result<Option<ID>, crate::LoroError> {
self.with_container_checked(ctx, |text| text.delete(ctx, pos, len)) self.with_container_checked(ctx, |text| text.delete(ctx, pos, len))
} }

View file

@ -1,7 +1,11 @@
use thiserror::Error; use thiserror::Error;
use crate::id::ClientID;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum LoroError { pub enum LoroError {
#[error("Context's client_id({found:?}) does not match Container's client_id({expected:?})")]
UnmatchedContext { expected: ClientID, found: ClientID },
// #[error("the data for key `{0}` is not available")] // #[error("the data for key `{0}` is not available")]
// Redaction(String), // Redaction(String),
// #[error("invalid header (expected {expected:?}, found {found:?})")] // #[error("invalid header (expected {expected:?}, found {found:?})")]

View file

@ -350,7 +350,9 @@ impl Actionable for Vec<Actor> {
container.insert(&actor.loro, &key.to_string(), *i); container.insert(&actor.loro, &key.to_string(), *i);
} }
FuzzValue::Container(c) => { FuzzValue::Container(c) => {
let new = container.insert_obj(&actor.loro, &key.to_string(), *c); let new = container
.insert_obj(&actor.loro, &key.to_string(), *c)
.unwrap();
actor.add_new_container(new); actor.add_new_container(new);
} }
} }
@ -380,7 +382,9 @@ impl Actionable for Vec<Actor> {
container.insert(&actor.loro, *key as usize, *i); container.insert(&actor.loro, *key as usize, *i);
} }
FuzzValue::Container(c) => { FuzzValue::Container(c) => {
let new = container.insert_obj(&actor.loro, *key as usize, *c); let new = container
.insert_obj(&actor.loro, *key as usize, *c)
.unwrap();
actor.add_new_container(new) actor.add_new_container(new)
} }
} }

View file

@ -148,9 +148,13 @@ impl From<ContainerID> for LoroValue {
pub mod wasm { pub mod wasm {
use fxhash::FxHashMap; use fxhash::FxHashMap;
use js_sys::{Array, Object}; use js_sys::{Array, Object};
use wasm_bindgen::prelude::*;
use wasm_bindgen::{JsCast, JsValue, __rt::IntoJsResult}; use wasm_bindgen::{JsCast, JsValue, __rt::IntoJsResult};
use crate::LoroValue; use crate::{container::ContainerID as _ContainerID, LoroValue};
#[wasm_bindgen]
pub struct ContainerID(_ContainerID);
pub fn convert(value: LoroValue) -> JsValue { pub fn convert(value: LoroValue) -> JsValue {
match value { match value {
@ -176,8 +180,10 @@ pub mod wasm {
map.into_js_result().unwrap() map.into_js_result().unwrap()
} }
LoroValue::Unresolved(_) => { LoroValue::Unresolved(container_id) => {
unreachable!() // FIXME:
// serde_wasm_bindgen::to_value(&container_id).unwrap()
ContainerID(*container_id).into()
} }
} }
} }

View file

@ -10,13 +10,13 @@ fn example() {
let mut doc = LoroCore::default(); let mut doc = LoroCore::default();
let mut list = doc.get_list("list"); let mut list = doc.get_list("list");
list.insert(&doc, 0, 123); list.insert(&doc, 0, 123).unwrap();
let map_id = list.insert_obj(&doc, 1, ContainerType::Map); let map_id = list.insert_obj(&doc, 1, ContainerType::Map).unwrap();
let mut map = doc.get_map(map_id); let mut map = doc.get_map(map_id);
let text = map.insert_obj(&doc, "map_b", ContainerType::Text); let text = map.insert_obj(&doc, "map_b", ContainerType::Text).unwrap();
let mut text = doc.get_text(text); let mut text = doc.get_text(text);
text.insert(&doc, 0, "world!"); text.insert(&doc, 0, "world!").unwrap();
text.insert(&doc, 0, "hello "); text.insert(&doc, 0, "hello ").unwrap();
assert_eq!( assert_eq!(
r#"[123,{"map_b":"hello world!"}]"#, r#"[123,{"map_b":"hello world!"}]"#,
list.get_value_deep(&doc).to_json() list.get_value_deep(&doc).to_json()
@ -30,11 +30,17 @@ fn list() {
let mut loro_b = LoroCore::default(); let mut loro_b = LoroCore::default();
let mut list_a = loro_a.get_list("list"); let mut list_a = loro_a.get_list("list");
let mut list_b = loro_b.get_list("list"); let mut list_b = loro_b.get_list("list");
list_a.insert_batch(&loro_a, 0, vec![12.into(), "haha".into()]); list_a
list_b.insert_batch(&loro_b, 0, vec![123.into(), "kk".into()]); .insert_batch(&loro_a, 0, vec![12.into(), "haha".into()])
let map_id = list_b.insert_obj(&loro_b, 1, loro_core::ContainerType::Map); .unwrap();
list_b
.insert_batch(&loro_b, 0, vec![123.into(), "kk".into()])
.unwrap();
let map_id = list_b
.insert_obj(&loro_b, 1, loro_core::ContainerType::Map)
.unwrap();
let mut map = loro_b.get_map(map_id); let mut map = loro_b.get_map(map_id);
map.insert(&loro_b, "map_b", 123); map.insert(&loro_b, "map_b", 123).unwrap();
println!("{}", list_a.get_value().to_json()); println!("{}", list_a.get_value().to_json());
println!("{}", list_b.get_value().to_json()); println!("{}", list_b.get_value().to_json());
loro_b.import(loro_a.export(loro_b.vv())); loro_b.import(loro_a.export(loro_b.vv()));
@ -49,7 +55,7 @@ fn list() {
fn map() { fn map() {
let mut loro = LoroCore::new(Default::default(), Some(10)); let mut loro = LoroCore::new(Default::default(), Some(10));
let mut root = loro.get_map("root"); let mut root = loro.get_map("root");
root.insert(&loro, "haha", 1.2); root.insert(&loro, "haha", 1.2).unwrap();
let value = root.get_value(); let value = root.get_value();
assert_eq!(value.as_map().unwrap().len(), 1); assert_eq!(value.as_map().unwrap().len(), 1);
assert_eq!( assert_eq!(
@ -62,7 +68,9 @@ fn map() {
.unwrap(), .unwrap(),
1.2 1.2
); );
let map_id = root.insert_obj(&loro, "map", loro_core::ContainerType::Map); let map_id = root
.insert_obj(&loro, "map", loro_core::ContainerType::Map)
.unwrap();
drop(root); drop(root);
let mut sub_map = loro.get_map(&map_id); let mut sub_map = loro.get_map(&map_id);
sub_map.insert(&loro, "sub", false); sub_map.insert(&loro, "sub", false);
@ -93,9 +101,9 @@ fn map() {
fn two_client_text_sync() { fn two_client_text_sync() {
let mut store = LoroCore::new(Default::default(), Some(10)); let mut store = LoroCore::new(Default::default(), Some(10));
let mut text_container = store.get_text("haha"); let mut text_container = store.get_text("haha");
text_container.insert(&store, 0, "012"); text_container.insert(&store, 0, "012").unwrap();
text_container.insert(&store, 1, "34"); text_container.insert(&store, 1, "34").unwrap();
text_container.insert(&store, 1, "56"); text_container.insert(&store, 1, "56").unwrap();
let value = text_container.get_value(); let value = text_container.get_value();
let value = value.as_string().unwrap(); let value = value.as_string().unwrap();
assert_eq!(&**value, "0563412"); assert_eq!(&**value, "0563412");
@ -110,8 +118,8 @@ fn two_client_text_sync() {
let value = value.as_string().unwrap(); let value = value.as_string().unwrap();
assert_eq!(&**value, "0563412"); assert_eq!(&**value, "0563412");
text_container.delete(&store_b, 0, 2); text_container.delete(&store_b, 0, 2).unwrap();
text_container.insert(&store_b, 4, "789"); text_container.insert(&store_b, 4, "789").unwrap();
let value = text_container.get_value(); let value = text_container.get_value();
let value = value.as_string().unwrap(); let value = value.as_string().unwrap();
assert_eq!(&**value, "63417892"); assert_eq!(&**value, "63417892");
@ -122,8 +130,8 @@ fn two_client_text_sync() {
let value = text_container.get_value(); let value = text_container.get_value();
let value = value.as_string().unwrap(); let value = value.as_string().unwrap();
assert_eq!(&**value, "63417892"); assert_eq!(&**value, "63417892");
text_container.delete(&store, 0, 8); text_container.delete(&store, 0, 8).unwrap();
text_container.insert(&store, 0, "abc"); text_container.insert(&store, 0, "abc").unwrap();
let value = text_container.get_value(); let value = text_container.get_value();
let value = value.as_string().unwrap(); let value = value.as_string().unwrap();
assert_eq!(&**value, "abc"); assert_eq!(&**value, "abc");
@ -143,8 +151,8 @@ fn test_recursive_should_panic() {
let mut store_b = LoroCore::new(Default::default(), Some(2)); let mut store_b = LoroCore::new(Default::default(), Some(2));
let mut text_a = store_a.get_text("text_a"); let mut text_a = store_a.get_text("text_a");
let mut text_b = store_b.get_text("text_b"); let mut text_b = store_b.get_text("text_b");
text_a.insert(&store_a, 0, "012"); text_a.insert(&store_a, 0, "012").unwrap();
text_b.insert(&store_a, 1, "34"); text_b.insert(&store_a, 1, "34").unwrap();
} }
#[ctor] #[ctor]

View file

@ -12,6 +12,10 @@
"https://deno.land/std@0.105.0/path/posix.ts": "b81974c768d298f8dcd2c720229639b3803ca4a241fa9a355c762fa2bc5ef0c1", "https://deno.land/std@0.105.0/path/posix.ts": "b81974c768d298f8dcd2c720229639b3803ca4a241fa9a355c762fa2bc5ef0c1",
"https://deno.land/std@0.105.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c", "https://deno.land/std@0.105.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c",
"https://deno.land/std@0.105.0/path/win32.ts": "f4a3d4a3f2c9fe894da046d5eac48b5e789a0ebec5152b2c0985efe96a9f7ae1", "https://deno.land/std@0.105.0/path/win32.ts": "f4a3d4a3f2c9fe894da046d5eac48b5e789a0ebec5152b2c0985efe96a9f7ae1",
"https://deno.land/std@0.165.0/fmt/colors.ts": "9e36a716611dcd2e4865adea9c4bec916b5c60caad4cdcdc630d4974e6bb8bd4",
"https://deno.land/std@0.165.0/testing/_diff.ts": "a23e7fc2b4d8daa3e158fa06856bedf5334ce2a2831e8bf9e509717f455adb2c",
"https://deno.land/std@0.165.0/testing/_format.ts": "cd11136e1797791045e639e9f0f4640d5b4166148796cad37e6ef75f7d7f3832",
"https://deno.land/std@0.165.0/testing/asserts.ts": "1e340c589853e82e0807629ba31a43c84ebdcdeca910c4a9705715dfdb0f5ce8",
"https://deno.land/x/dirname@1.1.2/mod.ts": "4029ca6b49da58d262d65f826ba9b3a89cc0b92a94c7220d5feb7bd34e498a54", "https://deno.land/x/dirname@1.1.2/mod.ts": "4029ca6b49da58d262d65f826ba9b3a89cc0b92a94c7220d5feb7bd34e498a54",
"https://deno.land/x/dirname@1.1.2/types.ts": "c1ed1667545bc4b1d69bdb2fc26a5fa8edae3a56e3081209c16a408a322a2319", "https://deno.land/x/dirname@1.1.2/types.ts": "c1ed1667545bc4b1d69bdb2fc26a5fa8edae3a56e3081209c16a408a322a2319",
"https://lra6z45nakk5lnu3yjchp7tftsdnwwikwr65ocha5eojfnlgu4sa.arweave.net/XEHs860CldW2m8JEd_5lnIbbWQq0fdcI4OkckrVmpyQ/_util/assert.ts": "e1f76e77c5ccb5a8e0dbbbe6cce3a56d2556c8cb5a9a8802fc9565af72462149", "https://lra6z45nakk5lnu3yjchp7tftsdnwwikwr65ocha5eojfnlgu4sa.arweave.net/XEHs860CldW2m8JEd_5lnIbbWQq0fdcI4OkckrVmpyQ/_util/assert.ts": "e1f76e77c5ccb5a8e0dbbbe6cce3a56d2556c8cb5a9a8802fc9565af72462149",

View file

@ -23,7 +23,9 @@ Deno.test({
b.set(loro, "ab", 123); b.set(loro, "ab", 123);
console.log(b.value); console.log(b.value);
console.log(a.value); console.log(a.value);
const bText = b.getText(loro, "hh"); const bText = b.getText(loro, "hh");
await t.step("getValueDeep", () => { await t.step("getValueDeep", () => {
bText.insert(loro, 0, "hello world Text"); bText.insert(loro, 0, "hello world Text");
assertEquals(b.getValueDeep(loro), { ab: 123, hh: "hello world Text" }); assertEquals(b.getValueDeep(loro), { ab: 123, hh: "hello world Text" });
@ -37,8 +39,12 @@ Deno.test({
}); });
await t.step("get value error", () => { await t.step("get value error", () => {
assertThrows(()=>{ // assertThrows(()=>{
const _ = bText.value; const map = loro.getMap("map");
}); const list = map.getList(loro, "list");
list.insert(loro, 0, 123);
console.log(map.value);
// });
}); });
}); });

View file

@ -18,6 +18,8 @@ pub fn set_panic_hook() {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
} }
type JsResult<T> = Result<T, JsError>;
#[wasm_bindgen] #[wasm_bindgen]
pub struct Loro(LoroCore); pub struct Loro(LoroCore);
@ -43,13 +45,13 @@ impl Loro {
} }
#[wasm_bindgen(js_name = "getText")] #[wasm_bindgen(js_name = "getText")]
pub fn get_text(&mut self, name: &str) -> Result<LoroText, JsValue> { pub fn get_text(&mut self, name: &str) -> JsResult<LoroText> {
let text = self.0.get_text(name); let text = self.0.get_text(name);
Ok(LoroText(text)) Ok(LoroText(text))
} }
#[wasm_bindgen(js_name = "getMap")] #[wasm_bindgen(js_name = "getMap")]
pub fn get_map(&mut self, name: &str) -> Result<LoroMap, JsValue> { pub fn get_map(&mut self, name: &str) -> JsResult<LoroMap> {
let map = self.0.get_map(name); let map = self.0.get_map(name);
Ok(LoroMap(map)) Ok(LoroMap(map))
} }
@ -60,16 +62,18 @@ pub struct LoroText(Text);
#[wasm_bindgen] #[wasm_bindgen]
impl LoroText { impl LoroText {
pub fn insert(&mut self, ctx: &Loro, index: usize, content: &str) { pub fn insert(&mut self, ctx: &Loro, index: usize, content: &str) -> JsResult<()> {
self.0.insert(ctx.deref(), index, content); self.0.insert(ctx.deref(), index, content)?;
Ok(())
} }
pub fn delete(&mut self, ctx: &Loro, index: usize, len: usize) { pub fn delete(&mut self, ctx: &Loro, index: usize, len: usize) -> JsResult<()> {
self.0.delete(ctx.deref(), index, len); self.0.delete(ctx.deref(), index, len)?;
Ok(())
} }
#[wasm_bindgen(js_name = "value", method, getter)] #[wasm_bindgen(js_name = "value", method, getter)]
pub fn get_value(&mut self) -> String { pub fn get_value(&self) -> String {
self.0.get_value().as_string().unwrap().to_string() self.0.get_value().as_string().unwrap().to_string()
} }
} }
@ -80,43 +84,54 @@ pub struct LoroMap(Map);
#[wasm_bindgen] #[wasm_bindgen]
impl LoroMap { impl LoroMap {
#[wasm_bindgen(js_name = "set")] #[wasm_bindgen(js_name = "set")]
pub fn insert(&mut self, ctx: &Loro, key: &str, value: JsValue) { pub fn insert(&mut self, ctx: &Loro, key: &str, value: JsValue) -> JsResult<()> {
self.0.insert(ctx.deref(), key, value); self.0.insert(ctx.deref(), key, value)?;
Ok(())
} }
pub fn delete(&mut self, ctx: &Loro, key: &str) { pub fn delete(&mut self, ctx: &Loro, key: &str) -> JsResult<()> {
self.0.delete(ctx.deref(), key); self.0.delete(ctx.deref(), key)?;
Ok(())
}
pub fn get(&self, key: &str) -> JsValue {
self.0.get(key).into()
} }
#[wasm_bindgen(js_name = "value", method, getter)] #[wasm_bindgen(js_name = "value", method, getter)]
pub fn get_value(&mut self) -> JsValue { pub fn get_value(&self) -> JsValue {
// TODO: if unresolved, return a container ID
self.0.get_value().into() self.0.get_value().into()
} }
#[wasm_bindgen(js_name = "getValueDeep")] #[wasm_bindgen(js_name = "getValueDeep")]
pub fn get_value_deep(&mut self, ctx: &Loro) -> JsValue { pub fn get_value_deep(&self, ctx: &Loro) -> JsValue {
self.0.get_value_deep(ctx.deref()).into() self.0.get_value_deep(ctx.deref()).into()
} }
#[wasm_bindgen(js_name = "getText")] #[wasm_bindgen(js_name = "getText")]
pub fn get_text(&mut self, ctx: &mut Loro, key: &str) -> LoroText { pub fn get_text(&mut self, ctx: &mut Loro, key: &str) -> JsResult<LoroText> {
let id = self.0.insert_obj(&ctx.0, key, ContainerType::Text); let id = self.0.insert_obj(&ctx.0, key, ContainerType::Text)?;
let text = ctx.deref().get_container(&id).unwrap(); let text = ctx.deref().get_container(&id).unwrap();
LoroText(Text::from_instance(text, ctx.deref().client_id())) Ok(LoroText(Text::from_instance(text, ctx.deref().client_id())))
} }
#[wasm_bindgen(js_name = "getMap")] #[wasm_bindgen(js_name = "getMap")]
pub fn get_map(&mut self, ctx: &mut Loro, key: &str) -> LoroMap { pub fn get_map(&mut self, ctx: &mut Loro, key: &str) -> JsResult<LoroMap> {
let id = self.0.insert_obj(ctx.deref_mut(), key, ContainerType::Map); let id = self
.0
.insert_obj(ctx.deref_mut(), key, ContainerType::Map)?;
let map = ctx.deref().get_container(&id).unwrap(); let map = ctx.deref().get_container(&id).unwrap();
LoroMap(Map::from_instance(map, ctx.deref().client_id())) Ok(LoroMap(Map::from_instance(map, ctx.deref().client_id())))
} }
#[wasm_bindgen(js_name = "getList")] #[wasm_bindgen(js_name = "getList")]
pub fn get_list(&mut self, ctx: &mut Loro, key: &str) -> LoroList { pub fn get_list(&mut self, ctx: &mut Loro, key: &str) -> JsResult<LoroList> {
let id = self.0.insert_obj(ctx.deref_mut(), key, ContainerType::List); let id = self
.0
.insert_obj(ctx.deref_mut(), key, ContainerType::List)?;
let list = ctx.deref().get_container(&id).unwrap(); let list = ctx.deref().get_container(&id).unwrap();
LoroList(List::from_instance(list, ctx.deref().client_id())) Ok(LoroList(List::from_instance(list, ctx.deref().client_id())))
} }
} }
@ -125,13 +140,18 @@ pub struct LoroList(List);
#[wasm_bindgen] #[wasm_bindgen]
impl LoroList { impl LoroList {
#[wasm_bindgen(js_name = "set")] pub fn insert(&mut self, ctx: &Loro, index: usize, value: JsValue) -> JsResult<()> {
pub fn insert(&mut self, ctx: &Loro, index: usize, value: JsValue) { self.0.insert(ctx.deref(), index, value)?;
self.0.insert(ctx.deref(), index, value); Ok(())
} }
pub fn delete(&mut self, ctx: &Loro, index: usize, len: usize) { pub fn delete(&mut self, ctx: &Loro, index: usize, len: usize) -> JsResult<()> {
self.0.delete(ctx.deref(), index, len); self.0.delete(ctx.deref(), index, len)?;
Ok(())
}
pub fn get(&self, index: usize) -> JsValue {
self.0.get(index).into()
} }
#[wasm_bindgen(js_name = "value", method, getter)] #[wasm_bindgen(js_name = "value", method, getter)]
@ -140,35 +160,35 @@ impl LoroList {
} }
#[wasm_bindgen(js_name = "getValueDeep")] #[wasm_bindgen(js_name = "getValueDeep")]
pub fn get_value_deep(&mut self, ctx: &Loro) -> JsValue { pub fn get_value_deep(&self, ctx: &Loro) -> JsValue {
self.0.get_value_deep(ctx.deref()).into() self.0.get_value_deep(ctx.deref()).into()
} }
#[wasm_bindgen(js_name = "getText")] #[wasm_bindgen(js_name = "getText")]
pub fn get_text(&mut self, ctx: &mut Loro, index: usize) -> LoroText { pub fn get_text(&mut self, ctx: &mut Loro, index: usize) -> JsResult<LoroText> {
let id = self let id = self
.0 .0
.insert_obj(ctx.deref_mut(), index, ContainerType::Text); .insert_obj(ctx.deref_mut(), index, ContainerType::Text)?;
let text = ctx.deref().get_container(&id).unwrap(); let text = ctx.deref().get_container(&id).unwrap();
LoroText(Text::from_instance(text, ctx.deref().client_id())) Ok(LoroText(Text::from_instance(text, ctx.deref().client_id())))
} }
#[wasm_bindgen(js_name = "getMap")] #[wasm_bindgen(js_name = "getMap")]
pub fn get_map(&mut self, ctx: &mut Loro, index: usize) -> LoroMap { pub fn get_map(&mut self, ctx: &mut Loro, index: usize) -> JsResult<LoroMap> {
let id = self let id = self
.0 .0
.insert_obj(ctx.deref_mut(), index, ContainerType::Map); .insert_obj(ctx.deref_mut(), index, ContainerType::Map)?;
let map = ctx.deref().get_container(&id).unwrap(); let map = ctx.deref().get_container(&id).unwrap();
LoroMap(Map::from_instance(map, ctx.deref().client_id())) Ok(LoroMap(Map::from_instance(map, ctx.deref().client_id())))
} }
#[wasm_bindgen(js_name = "getList")] #[wasm_bindgen(js_name = "getList")]
pub fn get_list(&mut self, ctx: &mut Loro, index: usize) -> LoroList { pub fn get_list(&mut self, ctx: &mut Loro, index: usize) -> JsResult<LoroList> {
let id = self let id = self
.0 .0
.insert_obj(ctx.deref_mut(), index, ContainerType::List); .insert_obj(ctx.deref_mut(), index, ContainerType::List)?;
let list = ctx.deref().get_container(&id).unwrap(); let list = ctx.deref().get_container(&id).unwrap();
LoroList(List::from_instance(list, ctx.deref().client_id())) Ok(LoroList(List::from_instance(list, ctx.deref().client_id())))
} }
} }