feat: get sub container directly when getting value (#175)

This commit is contained in:
Zixuan Chen 2023-11-16 21:46:57 +08:00 committed by GitHub
parent ef90e61ec5
commit 1ff1505933
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 103 additions and 19 deletions

View file

@ -38,6 +38,8 @@ pub enum LoroError {
ArgErr(Box<str>), ArgErr(Box<str>),
#[error("Auto commit has not started. The doc is readonly when detached. You should ensure autocommit is on and the doc and the state is attached.")] #[error("Auto commit has not started. The doc is readonly when detached. You should ensure autocommit is on and the doc and the state is attached.")]
AutoCommitNotStarted, AutoCommitNotStarted,
#[error("The doc is already dropped")]
DocDropError,
// #[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

@ -124,18 +124,24 @@ impl Handler {
impl Handler { impl Handler {
fn new( fn new(
txn: Weak<Mutex<Option<Transaction>>>, txn: Weak<Mutex<Option<Transaction>>>,
value: ContainerIdx, idx: ContainerIdx,
state: Weak<Mutex<DocState>>, state: Weak<Mutex<DocState>>,
) -> Self { ) -> Self {
match value.get_type() { match idx.get_type() {
ContainerType::Map => Self::Map(MapHandler::new(txn, value, state)), ContainerType::Map => Self::Map(MapHandler::new(txn, idx, state)),
ContainerType::List => Self::List(ListHandler::new(txn, value, state)), ContainerType::List => Self::List(ListHandler::new(txn, idx, state)),
ContainerType::Tree => Self::Tree(TreeHandler::new(txn, value, state)), ContainerType::Tree => Self::Tree(TreeHandler::new(txn, idx, state)),
ContainerType::Text => Self::Text(TextHandler::new(txn, value, state)), ContainerType::Text => Self::Text(TextHandler::new(txn, idx, state)),
} }
} }
} }
#[derive(Clone, EnumAsInner, Debug)]
pub enum ValueOrContainer {
Value(LoroValue),
Container(Handler),
}
impl TextHandler { impl TextHandler {
pub fn new( pub fn new(
txn: Weak<Mutex<Option<Transaction>>>, txn: Weak<Mutex<Option<Transaction>>>,
@ -758,6 +764,30 @@ impl ListHandler {
}) })
} }
/// Get value at given index, if it's a container, return a handler to the container
pub fn get_(&self, index: usize) -> Option<ValueOrContainer> {
let mutex = &self.state.upgrade().unwrap();
let doc_state = &mutex.lock().unwrap();
doc_state.with_state(self.container_idx, |state| {
let a = state.as_list_state().unwrap();
match a.get(index) {
Some(v) => {
if let LoroValue::Container(id) = v {
let idx = doc_state.arena.register_container(id);
Some(ValueOrContainer::Container(Handler::new(
self.txn.clone(),
idx,
self.state.clone(),
)))
} else {
Some(ValueOrContainer::Value(v.clone()))
}
}
None => None,
}
})
}
pub fn for_each<I>(&self, mut f: I) pub fn for_each<I>(&self, mut f: I)
where where
I: FnMut(&LoroValue), I: FnMut(&LoroValue),
@ -946,6 +976,28 @@ impl MapHandler {
}) })
} }
/// Get the value at given key, if value is a container, return a handler to the container
pub fn get_(&self, key: &str) -> Option<ValueOrContainer> {
let mutex = &self.state.upgrade().unwrap();
let doc_state = mutex.lock().unwrap();
doc_state.with_state(self.container_idx, |state| {
let a = state.as_map_state().unwrap();
let value = a.get(key);
match value {
Some(LoroValue::Container(container_id)) => {
let idx = doc_state.arena.register_container(container_id);
Some(ValueOrContainer::Container(Handler::new(
self.txn.clone(),
idx,
self.state.clone(),
)))
}
Some(value) => Some(ValueOrContainer::Value(value.clone())),
None => None,
}
})
}
pub fn id(&self) -> ContainerID { pub fn id(&self) -> ContainerID {
self.state self.state
.upgrade() .upgrade()

View file

@ -6,7 +6,9 @@ use loro_internal::{
ContainerID, ContainerID,
}, },
event::{Diff, Index}, event::{Diff, Index},
handler::{ListHandler, MapHandler, TextDelta, TextHandler, TreeHandler}, handler::{
Handler, ListHandler, MapHandler, TextDelta, TextHandler, TreeHandler, ValueOrContainer,
},
id::{Counter, PeerID, TreeID, ID}, id::{Counter, PeerID, TreeID, ID},
obs::SubID, obs::SubID,
version::Frontiers, version::Frontiers,
@ -1231,7 +1233,7 @@ impl LoroMap {
Ok(()) Ok(())
} }
/// Get the value of the key. /// Get the value of the key. If the value is a container, the corresponding handler will be returned.
/// ///
/// @example /// @example
/// ```ts /// ```ts
@ -1243,7 +1245,12 @@ impl LoroMap {
/// const bar = map.get("foo"); /// const bar = map.get("foo");
/// ``` /// ```
pub fn get(&self, key: &str) -> JsValue { pub fn get(&self, key: &str) -> JsValue {
self.0.get(key).into() let v = self.0.get_(key);
match v {
Some(ValueOrContainer::Container(c)) => handler_to_js_value(c),
Some(ValueOrContainer::Value(v)) => v.into(),
None => JsValue::UNDEFINED,
}
} }
/// Get the keys of the map. /// Get the keys of the map.
@ -1461,6 +1468,15 @@ impl LoroMap {
} }
} }
fn handler_to_js_value(handler: Handler) -> JsValue {
match handler {
Handler::Text(t) => LoroText(t).into(),
Handler::Map(m) => LoroMap(m).into(),
Handler::List(l) => LoroList(l).into(),
Handler::Tree(t) => LoroTree(t).into(),
}
}
/// The handler of a list container. /// The handler of a list container.
#[wasm_bindgen] #[wasm_bindgen]
pub struct LoroList(ListHandler); pub struct LoroList(ListHandler);
@ -1502,7 +1518,7 @@ impl LoroList {
Ok(()) Ok(())
} }
/// Get the value at the index. /// Get the value at the index. If the value is a container, the corresponding handler will be returned.
/// ///
/// @example /// @example
/// ```ts /// ```ts
@ -1515,11 +1531,14 @@ impl LoroList {
/// console.log(list.get(1)); // undefined /// console.log(list.get(1)); // undefined
/// ``` /// ```
pub fn get(&self, index: usize) -> JsValue { pub fn get(&self, index: usize) -> JsValue {
let Some(v) = self.0.get(index) else { let Some(v) = self.0.get_(index) else {
return JsValue::UNDEFINED; return JsValue::UNDEFINED;
}; };
JsValue::from(v) match v {
ValueOrContainer::Value(v) => v.into(),
ValueOrContainer::Container(h) => handler_to_js_value(h),
}
} }
/// Get the id of this container. /// Get the id of this container.

View file

@ -53,6 +53,7 @@ export type Value =
| Uint8Array | Uint8Array
| Value[]; | Value[];
export type Container = LoroList | LoroMap | LoroText | LoroTree;
export type Prelim = PrelimList | PrelimMap | PrelimText; export type Prelim = PrelimList | PrelimMap | PrelimText;
/** /**
@ -148,7 +149,7 @@ declare module "loro-wasm" {
insertContainer(pos: number, container: "Tree"): LoroTree; insertContainer(pos: number, container: "Tree"): LoroTree;
insertContainer(pos: number, container: string): never; insertContainer(pos: number, container: string): never;
get(index: number): Value; get(index: number): undefined | Value | Container;
getTyped<Key extends keyof T & number>(loro: Loro, index: Key): T[Key]; getTyped<Key extends keyof T & number>(loro: Loro, index: Key): T[Key];
insertTyped<Key extends keyof T & number>(pos: Key, value: T[Key]): void; insertTyped<Key extends keyof T & number>(pos: Key, value: T[Key]): void;
insert(pos: number, value: Value | Prelim): void; insert(pos: number, value: Value | Prelim): void;
@ -163,7 +164,7 @@ declare module "loro-wasm" {
setContainer(key: string, container_type: "Tree"): LoroTree; setContainer(key: string, container_type: "Tree"): LoroTree;
setContainer(key: string, container_type: string): never; setContainer(key: string, container_type: string): never;
get(key: string): Value; get(key: string): undefined | Value | Container;
getTyped<Key extends keyof T & string>(txn: Loro, key: Key): T[Key]; getTyped<Key extends keyof T & string>(txn: Loro, key: Key): T[Key];
set(key: string, value: Value | Prelim): void; set(key: string, value: Value | Prelim): void;
setTyped<Key extends keyof T & string>(key: Key, value: T[Key]): void; setTyped<Key extends keyof T & string>(key: Key, value: T[Key]): void;

View file

@ -84,14 +84,24 @@ describe("list", () => {
map.set("key", "value"); map.set("key", "value");
const v = list.get(0); const v = list.get(0);
console.log(v); console.log(v);
expect(typeof v).toBe("string"); expect(v instanceof LoroMap).toBeTruthy();
const m = doc.getMap(v as ContainerID); expect(v.getDeepValue()).toStrictEqual({ key: "value" });
expect(m.getDeepValue()).toStrictEqual({ key: "value" });
}); });
it.todo("iterate"); it.todo("iterate");
}); });
describe("map", () => {
it("get child container", () => {
const doc = new Loro();
const map = doc.getMap("map");
const list = map.setContainer("key", "List");
list.insert(0, 1);
expect(map.get("key") instanceof LoroList).toBeTruthy();
expect(map.get("key").getDeepValue()).toStrictEqual([1]);
});
});
describe("import", () => { describe("import", () => {
it("pending", () => { it("pending", () => {
const a = new Loro(); const a = new Loro();