mirror of
https://github.com/loro-dev/loro.git
synced 2025-01-23 05:24:51 +00:00
feat: get sub container directly when getting value (#175)
This commit is contained in:
parent
ef90e61ec5
commit
1ff1505933
5 changed files with 103 additions and 19 deletions
|
@ -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:?})")]
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue