diff --git a/Cargo.lock b/Cargo.lock index a2f81726..a03f91b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -788,6 +788,7 @@ dependencies = [ "ring", "rle", "serde", + "serde-wasm-bindgen", "serde_columnar", "serde_json", "smallvec", @@ -1483,6 +1484,17 @@ dependencies = [ "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]] name = "serde_columnar" version = "0.1.0" diff --git a/crates/loro-core/Cargo.toml b/crates/loro-core/Cargo.toml index 4935674d..e469f342 100644 --- a/crates/loro-core/Cargo.toml +++ b/crates/loro-core/Cargo.toml @@ -26,6 +26,7 @@ tabled = { version = "0.10.0", optional = true } colored = "2.0.0" bit-vec = "0.6.3" 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 } serde_json = { version = "1.0.87", optional = true } arref = "0.1.0" @@ -52,7 +53,7 @@ debug-log = "0.1.1" doctest = false [features] -wasm = ["wasm-bindgen", "js-sys"] +wasm = ["wasm-bindgen", "js-sys", "serde-wasm-bindgen"] json = ["serde_json"] # whether to use list slice instead of raw str in text container test_utils = ["crdt-list/fuzzing", "rand", "arbitrary", "tabled", "json"] diff --git a/crates/loro-core/src/container/list/list_container.rs b/crates/loro-core/src/container/list/list_container.rs index 55c82ae1..9a6485f9 100644 --- a/crates/loro-core/src/container/list/list_container.rs +++ b/crates/loro-core/src/container/list/list_container.rs @@ -23,6 +23,7 @@ use crate::{ op::{InnerContent, Op, RemoteContent, RichOp}, value::LoroValue, version::IdSpanVector, + LoroError, }; use super::list_op::InnerListOp; @@ -90,6 +91,13 @@ impl ListContainer { Some(id) } + pub fn get(&self, pos: usize) -> Option { + self.state + .get(pos) + .map(|range| self.raw_data.slice(&range.as_ref().0)) + .and_then(|slice| slice.first().cloned()) + } + pub fn delete(&mut self, ctx: &C, pos: usize, len: usize) -> Option { if len == 0 { return None; @@ -279,7 +287,12 @@ impl List { } } - pub fn insert_batch(&mut self, ctx: &C, pos: usize, values: Vec) { + pub fn insert_batch( + &mut self, + ctx: &C, + pos: usize, + values: Vec, + ) -> Result<(), LoroError> { self.with_container_checked(ctx, |x| x.insert_batch(ctx, pos, values)) } @@ -288,7 +301,7 @@ impl List { ctx: &C, pos: usize, value: V, - ) -> Option { + ) -> Result, LoroError> { self.with_container_checked(ctx, |x| x.insert(ctx, pos, value)) } @@ -297,12 +310,21 @@ impl List { ctx: &C, pos: usize, obj: ContainerType, - ) -> ContainerID { + ) -> Result { self.with_container_checked(ctx, |x| x.insert_obj(ctx, pos, obj)) } - pub fn delete(&mut self, ctx: &C, pos: usize, len: usize) -> Option { - self.with_container_checked(ctx, |text| text.delete(ctx, pos, len)) + pub fn delete( + &mut self, + ctx: &C, + pos: usize, + len: usize, + ) -> Result, LoroError> { + self.with_container_checked(ctx, |list| list.delete(ctx, pos, len)) + } + + pub fn get(&self, pos: usize) -> Option { + self.with_container(|list| list.get(pos)) } pub fn len(&self) -> usize { diff --git a/crates/loro-core/src/container/map/map_container.rs b/crates/loro-core/src/container/map/map_container.rs index 1420ed0f..6b60d1b5 100644 --- a/crates/loro-core/src/container/map/map_container.rs +++ b/crates/loro-core/src/container/map/map_container.rs @@ -117,6 +117,11 @@ impl MapContainer { pub fn delete(&mut self, ctx: &C, key: InternalString) { self.insert(ctx, key, LoroValue::Null); } + + #[inline] + pub fn get(&self, key: &InternalString) -> Option { + self.state.get(key).map(|v| &v.value).cloned() + } } impl Container for MapContainer { @@ -230,7 +235,12 @@ impl Map { } } - pub fn insert>(&mut self, ctx: &C, key: &str, value: V) { + pub fn insert>( + &mut self, + ctx: &C, + key: &str, + value: V, + ) -> Result<(), crate::LoroError> { self.with_container_checked(ctx, |map| { map.insert(ctx, key.into(), value); }) @@ -241,16 +251,20 @@ impl Map { ctx: &C, key: &str, obj: ContainerType, - ) -> ContainerID { + ) -> Result { self.with_container_checked(ctx, |map| map.insert_obj(ctx, key.into(), obj)) } - pub fn delete(&mut self, ctx: &C, key: &str) { + pub fn delete(&mut self, ctx: &C, key: &str) -> Result<(), crate::LoroError> { self.with_container_checked(ctx, |map| { map.delete(ctx, key.into()); }) } + pub fn get(&self, key: &str) -> Option { + self.with_container(|map| map.get(&key.into())) + } + pub fn id(&self) -> ContainerID { self.instance.lock().unwrap().as_map().unwrap().id.clone() } diff --git a/crates/loro-core/src/container/registry.rs b/crates/loro-core/src/container/registry.rs index a81e0d78..16bd8033 100644 --- a/crates/loro-core/src/container/registry.rs +++ b/crates/loro-core/src/container/registry.rs @@ -14,7 +14,7 @@ use crate::{ id::{ClientID, ContainerIdx}, op::{RemoteContent, RichOp}, version::IdSpanVector, - LoroValue, VersionVector, + LoroError, LoroValue, VersionVector, }; use super::{ @@ -297,19 +297,18 @@ pub trait ContainerWrapper { where F: FnOnce(&mut Self::Container) -> R; - fn with_container_checked(&self, ctx: &C, f: F) -> R + fn with_container_checked(&self, ctx: &C, f: F) -> Result where F: FnOnce(&mut Self::Container) -> R, { let store_client_id = ctx.log_store().read().unwrap().this_client_id(); if store_client_id != self.client_id() { - panic!( - "Context's client_id({}) does not match Container's client_id({})", - store_client_id, - self.client_id() - ); + return Err(LoroError::UnmatchedContext { + expected: self.client_id(), + found: store_client_id, + }); } - self.with_container(f) + Ok(self.with_container(f)) } fn client_id(&self) -> ClientID; diff --git a/crates/loro-core/src/container/text/text_container.rs b/crates/loro-core/src/container/text/text_container.rs index bab010d9..0467c9af 100644 --- a/crates/loro-core/src/container/text/text_container.rs +++ b/crates/loro-core/src/container/text/text_container.rs @@ -302,11 +302,21 @@ impl Text { self.instance.lock().unwrap().as_text().unwrap().id.clone() } - pub fn insert(&mut self, ctx: &C, pos: usize, text: &str) -> Option { + pub fn insert( + &mut self, + ctx: &C, + pos: usize, + text: &str, + ) -> Result, crate::LoroError> { self.with_container_checked(ctx, |x| x.insert(ctx, pos, text)) } - pub fn delete(&mut self, ctx: &C, pos: usize, len: usize) -> Option { + pub fn delete( + &mut self, + ctx: &C, + pos: usize, + len: usize, + ) -> Result, crate::LoroError> { self.with_container_checked(ctx, |text| text.delete(ctx, pos, len)) } diff --git a/crates/loro-core/src/error.rs b/crates/loro-core/src/error.rs index c0d81e1e..8554b8d7 100644 --- a/crates/loro-core/src/error.rs +++ b/crates/loro-core/src/error.rs @@ -1,7 +1,11 @@ use thiserror::Error; +use crate::id::ClientID; + #[derive(Error, Debug)] 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")] // Redaction(String), // #[error("invalid header (expected {expected:?}, found {found:?})")] diff --git a/crates/loro-core/src/fuzz/recursive.rs b/crates/loro-core/src/fuzz/recursive.rs index f6ae955e..b5f021c4 100644 --- a/crates/loro-core/src/fuzz/recursive.rs +++ b/crates/loro-core/src/fuzz/recursive.rs @@ -350,7 +350,9 @@ impl Actionable for Vec { container.insert(&actor.loro, &key.to_string(), *i); } 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); } } @@ -380,7 +382,9 @@ impl Actionable for Vec { container.insert(&actor.loro, *key as usize, *i); } 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) } } diff --git a/crates/loro-core/src/value.rs b/crates/loro-core/src/value.rs index c160fbe8..89bf683b 100644 --- a/crates/loro-core/src/value.rs +++ b/crates/loro-core/src/value.rs @@ -148,9 +148,13 @@ impl From for LoroValue { pub mod wasm { use fxhash::FxHashMap; use js_sys::{Array, Object}; + use wasm_bindgen::prelude::*; 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 { match value { @@ -176,8 +180,10 @@ pub mod wasm { map.into_js_result().unwrap() } - LoroValue::Unresolved(_) => { - unreachable!() + LoroValue::Unresolved(container_id) => { + // FIXME: + // serde_wasm_bindgen::to_value(&container_id).unwrap() + ContainerID(*container_id).into() } } } diff --git a/crates/loro-core/tests/test.rs b/crates/loro-core/tests/test.rs index b884c9b5..07c052e0 100644 --- a/crates/loro-core/tests/test.rs +++ b/crates/loro-core/tests/test.rs @@ -10,13 +10,13 @@ fn example() { let mut doc = LoroCore::default(); let mut list = doc.get_list("list"); - list.insert(&doc, 0, 123); - let map_id = list.insert_obj(&doc, 1, ContainerType::Map); + list.insert(&doc, 0, 123).unwrap(); + let map_id = list.insert_obj(&doc, 1, ContainerType::Map).unwrap(); 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); - text.insert(&doc, 0, "world!"); - text.insert(&doc, 0, "hello "); + text.insert(&doc, 0, "world!").unwrap(); + text.insert(&doc, 0, "hello ").unwrap(); assert_eq!( r#"[123,{"map_b":"hello world!"}]"#, list.get_value_deep(&doc).to_json() @@ -30,11 +30,17 @@ fn list() { let mut loro_b = LoroCore::default(); let mut list_a = loro_a.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_b.insert_batch(&loro_b, 0, vec![123.into(), "kk".into()]); - let map_id = list_b.insert_obj(&loro_b, 1, loro_core::ContainerType::Map); + list_a + .insert_batch(&loro_a, 0, vec![12.into(), "haha".into()]) + .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); - map.insert(&loro_b, "map_b", 123); + map.insert(&loro_b, "map_b", 123).unwrap(); println!("{}", list_a.get_value().to_json()); println!("{}", list_b.get_value().to_json()); loro_b.import(loro_a.export(loro_b.vv())); @@ -49,7 +55,7 @@ fn list() { fn map() { let mut loro = LoroCore::new(Default::default(), Some(10)); 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(); assert_eq!(value.as_map().unwrap().len(), 1); assert_eq!( @@ -62,7 +68,9 @@ fn map() { .unwrap(), 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); let mut sub_map = loro.get_map(&map_id); sub_map.insert(&loro, "sub", false); @@ -93,9 +101,9 @@ fn map() { fn two_client_text_sync() { let mut store = LoroCore::new(Default::default(), Some(10)); let mut text_container = store.get_text("haha"); - text_container.insert(&store, 0, "012"); - text_container.insert(&store, 1, "34"); - text_container.insert(&store, 1, "56"); + text_container.insert(&store, 0, "012").unwrap(); + text_container.insert(&store, 1, "34").unwrap(); + text_container.insert(&store, 1, "56").unwrap(); let value = text_container.get_value(); let value = value.as_string().unwrap(); assert_eq!(&**value, "0563412"); @@ -110,8 +118,8 @@ fn two_client_text_sync() { let value = value.as_string().unwrap(); assert_eq!(&**value, "0563412"); - text_container.delete(&store_b, 0, 2); - text_container.insert(&store_b, 4, "789"); + text_container.delete(&store_b, 0, 2).unwrap(); + text_container.insert(&store_b, 4, "789").unwrap(); let value = text_container.get_value(); let value = value.as_string().unwrap(); assert_eq!(&**value, "63417892"); @@ -122,8 +130,8 @@ fn two_client_text_sync() { let value = text_container.get_value(); let value = value.as_string().unwrap(); assert_eq!(&**value, "63417892"); - text_container.delete(&store, 0, 8); - text_container.insert(&store, 0, "abc"); + text_container.delete(&store, 0, 8).unwrap(); + text_container.insert(&store, 0, "abc").unwrap(); let value = text_container.get_value(); let value = value.as_string().unwrap(); 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 text_a = store_a.get_text("text_a"); let mut text_b = store_b.get_text("text_b"); - text_a.insert(&store_a, 0, "012"); - text_b.insert(&store_a, 1, "34"); + text_a.insert(&store_a, 0, "012").unwrap(); + text_b.insert(&store_a, 1, "34").unwrap(); } #[ctor] diff --git a/crates/loro-wasm/deno.lock b/crates/loro-wasm/deno.lock index 86befe69..adc978c5 100644 --- a/crates/loro-wasm/deno.lock +++ b/crates/loro-wasm/deno.lock @@ -12,6 +12,10 @@ "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/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/types.ts": "c1ed1667545bc4b1d69bdb2fc26a5fa8edae3a56e3081209c16a408a322a2319", "https://lra6z45nakk5lnu3yjchp7tftsdnwwikwr65ocha5eojfnlgu4sa.arweave.net/XEHs860CldW2m8JEd_5lnIbbWQq0fdcI4OkckrVmpyQ/_util/assert.ts": "e1f76e77c5ccb5a8e0dbbbe6cce3a56d2556c8cb5a9a8802fc9565af72462149", diff --git a/crates/loro-wasm/deno_test/test.ts b/crates/loro-wasm/deno_test/test.ts index e64f2328..adf36813 100644 --- a/crates/loro-wasm/deno_test/test.ts +++ b/crates/loro-wasm/deno_test/test.ts @@ -23,7 +23,9 @@ Deno.test({ b.set(loro, "ab", 123); console.log(b.value); console.log(a.value); + const bText = b.getText(loro, "hh"); + await t.step("getValueDeep", () => { bText.insert(loro, 0, "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", () => { - assertThrows(()=>{ - const _ = bText.value; - }); + // assertThrows(()=>{ + const map = loro.getMap("map"); + const list = map.getList(loro, "list"); + list.insert(loro, 0, 123); + console.log(map.value); + + // }); }); }); diff --git a/crates/loro-wasm/src/lib.rs b/crates/loro-wasm/src/lib.rs index 93fdfbda..a9a2d6fb 100644 --- a/crates/loro-wasm/src/lib.rs +++ b/crates/loro-wasm/src/lib.rs @@ -18,6 +18,8 @@ pub fn set_panic_hook() { console_error_panic_hook::set_once(); } +type JsResult = Result; + #[wasm_bindgen] pub struct Loro(LoroCore); @@ -43,13 +45,13 @@ impl Loro { } #[wasm_bindgen(js_name = "getText")] - pub fn get_text(&mut self, name: &str) -> Result { + pub fn get_text(&mut self, name: &str) -> JsResult { let text = self.0.get_text(name); Ok(LoroText(text)) } #[wasm_bindgen(js_name = "getMap")] - pub fn get_map(&mut self, name: &str) -> Result { + pub fn get_map(&mut self, name: &str) -> JsResult { let map = self.0.get_map(name); Ok(LoroMap(map)) } @@ -60,16 +62,18 @@ pub struct LoroText(Text); #[wasm_bindgen] impl LoroText { - pub fn insert(&mut self, ctx: &Loro, index: usize, content: &str) { - self.0.insert(ctx.deref(), index, content); + pub fn insert(&mut self, ctx: &Loro, index: usize, content: &str) -> JsResult<()> { + self.0.insert(ctx.deref(), index, content)?; + Ok(()) } - pub fn delete(&mut self, ctx: &Loro, index: usize, len: usize) { - self.0.delete(ctx.deref(), index, len); + pub fn delete(&mut self, ctx: &Loro, index: usize, len: usize) -> JsResult<()> { + self.0.delete(ctx.deref(), index, len)?; + Ok(()) } #[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() } } @@ -80,43 +84,54 @@ pub struct LoroMap(Map); #[wasm_bindgen] impl LoroMap { #[wasm_bindgen(js_name = "set")] - pub fn insert(&mut self, ctx: &Loro, key: &str, value: JsValue) { - self.0.insert(ctx.deref(), key, value); + pub fn insert(&mut self, ctx: &Loro, key: &str, value: JsValue) -> JsResult<()> { + self.0.insert(ctx.deref(), key, value)?; + Ok(()) } - pub fn delete(&mut self, ctx: &Loro, key: &str) { - self.0.delete(ctx.deref(), key); + pub fn delete(&mut self, ctx: &Loro, key: &str) -> JsResult<()> { + 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)] - 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() } #[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() } #[wasm_bindgen(js_name = "getText")] - pub fn get_text(&mut self, ctx: &mut Loro, key: &str) -> LoroText { - let id = self.0.insert_obj(&ctx.0, key, ContainerType::Text); + pub fn get_text(&mut self, ctx: &mut Loro, key: &str) -> JsResult { + let id = self.0.insert_obj(&ctx.0, key, ContainerType::Text)?; 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")] - pub fn get_map(&mut self, ctx: &mut Loro, key: &str) -> LoroMap { - let id = self.0.insert_obj(ctx.deref_mut(), key, ContainerType::Map); + pub fn get_map(&mut self, ctx: &mut Loro, key: &str) -> JsResult { + let id = self + .0 + .insert_obj(ctx.deref_mut(), key, ContainerType::Map)?; 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")] - pub fn get_list(&mut self, ctx: &mut Loro, key: &str) -> LoroList { - let id = self.0.insert_obj(ctx.deref_mut(), key, ContainerType::List); + pub fn get_list(&mut self, ctx: &mut Loro, key: &str) -> JsResult { + let id = self + .0 + .insert_obj(ctx.deref_mut(), key, ContainerType::List)?; 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] impl LoroList { - #[wasm_bindgen(js_name = "set")] - pub fn insert(&mut self, ctx: &Loro, index: usize, value: JsValue) { - self.0.insert(ctx.deref(), index, value); + pub fn insert(&mut self, ctx: &Loro, index: usize, value: JsValue) -> JsResult<()> { + self.0.insert(ctx.deref(), index, value)?; + Ok(()) } - pub fn delete(&mut self, ctx: &Loro, index: usize, len: usize) { - self.0.delete(ctx.deref(), index, len); + pub fn delete(&mut self, ctx: &Loro, index: usize, len: usize) -> JsResult<()> { + 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)] @@ -140,35 +160,35 @@ impl LoroList { } #[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() } #[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 { let id = self .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(); - LoroText(Text::from_instance(text, ctx.deref().client_id())) + Ok(LoroText(Text::from_instance(text, ctx.deref().client_id()))) } #[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 { let id = self .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(); - LoroMap(Map::from_instance(map, ctx.deref().client_id())) + Ok(LoroMap(Map::from_instance(map, ctx.deref().client_id()))) } #[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 { let id = self .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(); - LoroList(List::from_instance(list, ctx.deref().client_id())) + Ok(LoroList(List::from_instance(list, ctx.deref().client_id()))) } }