mirror of
https://github.com/loro-dev/loro.git
synced 2024-11-28 01:06:50 +00:00
test: add compatibility tests (#503)
This commit is contained in:
parent
9bfe97bce4
commit
66b8a76fdf
10 changed files with 558 additions and 16 deletions
131
Cargo.lock
generated
131
Cargo.lock
generated
|
@ -700,6 +700,7 @@ dependencies = [
|
|||
"fxhash",
|
||||
"itertools 0.12.1",
|
||||
"loro 0.16.12",
|
||||
"loro 0.16.12 (git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4)",
|
||||
"loro 0.16.2 (git+https://github.com/loro-dev/loro.git?tag=loro-crdt@0.16.7)",
|
||||
"loro 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=90470658435ec4c62b5af59ebb82fe9e1f5aa761)",
|
||||
"num_cpus",
|
||||
|
@ -1058,13 +1059,27 @@ dependencies = [
|
|||
"loro-common 0.16.12",
|
||||
"loro-delta 0.16.12",
|
||||
"loro-internal 0.16.12",
|
||||
"loro-kv-store",
|
||||
"loro-kv-store 0.16.2",
|
||||
"pretty_assertions",
|
||||
"rand",
|
||||
"serde_json",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loro"
|
||||
version = "0.16.12"
|
||||
source = "git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4#9bfe97bce4912c6dc8439817497d18423a0e8cb7"
|
||||
dependencies = [
|
||||
"enum-as-inner 0.6.0",
|
||||
"generic-btree",
|
||||
"loro-common 0.16.12 (git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4)",
|
||||
"loro-delta 0.16.12 (git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4)",
|
||||
"loro-internal 0.16.12 (git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4)",
|
||||
"loro-kv-store 0.16.2 (git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4)",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loro-common"
|
||||
version = "0.16.2"
|
||||
|
@ -1116,6 +1131,24 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loro-common"
|
||||
version = "0.16.12"
|
||||
source = "git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4#9bfe97bce4912c6dc8439817497d18423a0e8cb7"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"enum-as-inner 0.6.0",
|
||||
"fxhash",
|
||||
"leb128",
|
||||
"loro-rle 0.16.12 (git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4)",
|
||||
"nonmax",
|
||||
"serde",
|
||||
"serde_columnar",
|
||||
"serde_json",
|
||||
"string_cache",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loro-delta"
|
||||
version = "0.16.2"
|
||||
|
@ -1156,6 +1189,18 @@ dependencies = [
|
|||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loro-delta"
|
||||
version = "0.16.12"
|
||||
source = "git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4#9bfe97bce4912c6dc8439817497d18423a0e8cb7"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"enum-as-inner 0.5.1",
|
||||
"generic-btree",
|
||||
"heapless 0.8.0",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loro-ffi"
|
||||
version = "0.16.2"
|
||||
|
@ -1262,7 +1307,7 @@ dependencies = [
|
|||
"leb128",
|
||||
"loro-common 0.16.12",
|
||||
"loro-delta 0.16.12",
|
||||
"loro-kv-store",
|
||||
"loro-kv-store 0.16.2",
|
||||
"loro-rle 0.16.12",
|
||||
"loro_fractional_index 0.16.12",
|
||||
"md5",
|
||||
|
@ -1291,6 +1336,47 @@ dependencies = [
|
|||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loro-internal"
|
||||
version = "0.16.12"
|
||||
source = "git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4#9bfe97bce4912c6dc8439817497d18423a0e8cb7"
|
||||
dependencies = [
|
||||
"append-only-bytes",
|
||||
"arref",
|
||||
"bytes",
|
||||
"either",
|
||||
"ensure-cov",
|
||||
"enum-as-inner 0.6.0",
|
||||
"enum_dispatch",
|
||||
"fxhash",
|
||||
"generic-btree",
|
||||
"getrandom",
|
||||
"im",
|
||||
"itertools 0.12.1",
|
||||
"leb128",
|
||||
"loro-common 0.16.12 (git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4)",
|
||||
"loro-delta 0.16.12 (git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4)",
|
||||
"loro-kv-store 0.16.2 (git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4)",
|
||||
"loro-rle 0.16.12 (git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4)",
|
||||
"loro_fractional_index 0.16.12 (git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4)",
|
||||
"md5",
|
||||
"nonmax",
|
||||
"num",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"postcard",
|
||||
"pretty_assertions",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_columnar",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"xxhash-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loro-kv-store"
|
||||
version = "0.16.2"
|
||||
|
@ -1309,6 +1395,22 @@ dependencies = [
|
|||
"xxhash-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loro-kv-store"
|
||||
version = "0.16.2"
|
||||
source = "git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4#9bfe97bce4912c6dc8439817497d18423a0e8cb7"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"ensure-cov",
|
||||
"fxhash",
|
||||
"loro-common 0.16.12 (git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4)",
|
||||
"lz4_flex",
|
||||
"once_cell",
|
||||
"quick_cache",
|
||||
"tracing",
|
||||
"xxhash-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loro-rle"
|
||||
version = "0.16.2"
|
||||
|
@ -1352,6 +1454,19 @@ dependencies = [
|
|||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loro-rle"
|
||||
version = "0.16.12"
|
||||
source = "git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4#9bfe97bce4912c6dc8439817497d18423a0e8cb7"
|
||||
dependencies = [
|
||||
"append-only-bytes",
|
||||
"arref",
|
||||
"enum-as-inner 0.6.0",
|
||||
"fxhash",
|
||||
"num",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loro-thunderdome"
|
||||
version = "0.6.2"
|
||||
|
@ -1411,6 +1526,18 @@ dependencies = [
|
|||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loro_fractional_index"
|
||||
version = "0.16.12"
|
||||
source = "git+https://github.com/loro-dev/loro.git?tag=loro-crdt@1.0.0-alpha.4#9bfe97bce4912c6dc8439817497d18423a0e8cb7"
|
||||
dependencies = [
|
||||
"imbl",
|
||||
"once_cell",
|
||||
"rand",
|
||||
"serde",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lz4_flex"
|
||||
version = "0.11.3"
|
||||
|
|
|
@ -24,6 +24,7 @@ rayon = "1.10.0"
|
|||
bytes = "1"
|
||||
ensure-cov = { workspace = true }
|
||||
pretty_assertions = "1.4.0"
|
||||
loro-alpha-4 = { git = "https://github.com/loro-dev/loro.git", tag = "loro-crdt@1.0.0-alpha.4", package = "loro" }
|
||||
|
||||
[dev-dependencies]
|
||||
ctor = "0.2"
|
||||
|
|
|
@ -149,3 +149,286 @@ fn snapshot_from_016_can_be_imported_in_cur_version() {
|
|||
doc_current.get_deep_value().to_json()
|
||||
);
|
||||
}
|
||||
|
||||
/// Macro to generate random operations on a LoroDoc.
|
||||
///
|
||||
/// This macro creates a series of random operations on various container types
|
||||
/// within a LoroDoc. It's useful for testing and fuzzing purposes.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `$doc` - A reference to a `loro::LoroDoc` instance.
|
||||
/// * `$seed` - A `u64` value used to seed the random number generator.
|
||||
/// * `$len` - A `usize` value specifying the number of random operations to perform.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let doc = loro::LoroDoc::new();
|
||||
/// gen_random_ops!(doc, 12345, 100);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! gen_random_ops {
|
||||
($doc:expr, $seed:expr, $len:expr) => {{
|
||||
use rand::rngs::StdRng;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::{Rng, SeedableRng};
|
||||
|
||||
let mut rng = StdRng::seed_from_u64($seed);
|
||||
let containers = ["text", "map", "list", "tree", "movable_list"];
|
||||
|
||||
for _ in 0..$len {
|
||||
let container = containers.choose(&mut rng).unwrap();
|
||||
match *container {
|
||||
"text" => {
|
||||
let text = $doc.get_text("text");
|
||||
let pos = rng.gen_range(0..=text.len_unicode());
|
||||
let content = (0..5).map(|_| rng.gen::<char>()).collect::<String>();
|
||||
if rng.gen_bool(0.7) {
|
||||
text.insert(pos, &content).unwrap_or_default();
|
||||
} else {
|
||||
let del_len = rng.gen_range(0..=text.len_unicode().saturating_sub(pos));
|
||||
text.delete(pos, del_len).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
"map" => {
|
||||
let map = $doc.get_map("map");
|
||||
let key = format!("key_{}", rng.gen::<u32>());
|
||||
if rng.gen_bool(0.7) {
|
||||
let value = format!("value_{}", rng.gen::<u32>());
|
||||
map.insert(&key, value).unwrap();
|
||||
} else if !map.is_empty() {
|
||||
let v = map.get_value();
|
||||
let existing_key = v
|
||||
.as_map()
|
||||
.unwrap()
|
||||
.keys()
|
||||
.nth(rng.gen_range(0..map.len()))
|
||||
.unwrap();
|
||||
map.delete(&existing_key).unwrap();
|
||||
}
|
||||
}
|
||||
"list" => {
|
||||
let list = $doc.get_list("list");
|
||||
let pos = rng.gen_range(0..=list.len());
|
||||
if rng.gen_bool(0.7) {
|
||||
let value = rng.gen::<i32>();
|
||||
list.insert(pos, value).unwrap();
|
||||
} else if !list.is_empty() {
|
||||
list.delete(pos, 1).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
"tree" => {
|
||||
let tree = $doc.get_tree("tree");
|
||||
tree.enable_fractional_index(0);
|
||||
let nodes: Vec<_> = tree.nodes();
|
||||
if nodes.is_empty() {
|
||||
tree.create(None).unwrap();
|
||||
} else {
|
||||
let node = nodes.choose(&mut rng).unwrap();
|
||||
match rng.gen_range(0..3) {
|
||||
0 => {
|
||||
tree.create(Some(*node)).unwrap();
|
||||
}
|
||||
1 if !tree.is_node_deleted(node).unwrap() => {
|
||||
tree.delete(*node).unwrap_or_default();
|
||||
}
|
||||
_ => {
|
||||
if let Some(sibling) = nodes.choose(&mut rng) {
|
||||
if sibling != node
|
||||
&& !tree.is_node_deleted(sibling).unwrap()
|
||||
&& !tree.is_node_deleted(node).unwrap()
|
||||
{
|
||||
tree.mov_before(*node, *sibling).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"movable_list" => {
|
||||
let movable_list = $doc.get_movable_list("movable_list");
|
||||
let pos = rng.gen_range(0..=movable_list.len());
|
||||
match rng.gen_range(0..3) {
|
||||
0 => {
|
||||
let value = rng.gen::<i32>();
|
||||
movable_list.insert(pos, value).unwrap();
|
||||
}
|
||||
1 => {
|
||||
if !movable_list.is_empty() {
|
||||
movable_list.delete(pos, 1).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
if !movable_list.is_empty() {
|
||||
let from = rng.gen_range(0..movable_list.len());
|
||||
let to = rng.gen_range(0..=movable_list.len());
|
||||
movable_list.mov(from, to).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
_ => unreachable!("unreachable movable list op"),
|
||||
}
|
||||
}
|
||||
_ => unreachable!("unreachable container type"),
|
||||
}
|
||||
}
|
||||
$doc.commit();
|
||||
}};
|
||||
}
|
||||
#[ctor::ctor]
|
||||
fn init() {
|
||||
dev_utils::setup_test_log();
|
||||
}
|
||||
|
||||
mod compatibility_with_10_alpha_4 {
|
||||
use super::*;
|
||||
use loro_alpha_4::{self, ToJson};
|
||||
|
||||
#[test]
|
||||
fn test_shallow_snapshot() {
|
||||
let doc1 = loro::LoroDoc::new();
|
||||
doc1.set_peer_id(1).unwrap();
|
||||
gen_random_ops!(doc1, 12345, 100);
|
||||
let snapshot = doc1
|
||||
.export(loro::ExportMode::shallow_snapshot_since(ID::new(1, 50)))
|
||||
.unwrap();
|
||||
|
||||
let doc2 = loro_alpha_4::LoroDoc::new();
|
||||
doc2.import(&snapshot).unwrap();
|
||||
assert_eq!(
|
||||
doc1.get_deep_value().to_json_value(),
|
||||
doc2.get_deep_value().to_json_value()
|
||||
);
|
||||
assert_eq!(
|
||||
doc2.shallow_since_frontiers().encode(),
|
||||
loro::Frontiers::from_id(ID::new(1, 50)).encode()
|
||||
);
|
||||
|
||||
gen_random_ops!(doc2, 12345, 100);
|
||||
let snapshot = doc2
|
||||
.export(loro_alpha_4::ExportMode::all_updates())
|
||||
.unwrap();
|
||||
doc1.import(&snapshot).unwrap();
|
||||
assert_eq!(
|
||||
doc1.get_deep_value().to_json_value(),
|
||||
doc2.get_deep_value().to_json_value()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shallow_snapshot_mirrored() {
|
||||
let doc1 = loro_alpha_4::LoroDoc::new();
|
||||
doc1.set_peer_id(1).unwrap();
|
||||
gen_random_ops!(doc1, 1234, 100);
|
||||
let snapshot = doc1
|
||||
.export(loro_alpha_4::ExportMode::shallow_snapshot_since(
|
||||
loro_alpha_4::ID::new(1, 50),
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let doc2 = loro::LoroDoc::new();
|
||||
doc2.import(&snapshot).unwrap();
|
||||
assert_eq!(
|
||||
doc1.get_deep_value().to_json_value(),
|
||||
doc2.get_deep_value().to_json_value()
|
||||
);
|
||||
assert_eq!(
|
||||
doc2.shallow_since_frontiers().encode(),
|
||||
loro_alpha_4::Frontiers::from_id(loro_alpha_4::ID::new(1, 50)).encode()
|
||||
);
|
||||
|
||||
gen_random_ops!(doc2, 1234, 100);
|
||||
let snapshot = doc2.export(loro::ExportMode::all_updates()).unwrap();
|
||||
doc1.import(&snapshot).unwrap();
|
||||
assert_eq!(
|
||||
doc1.get_deep_value().to_json_value(),
|
||||
doc2.get_deep_value().to_json_value()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_snapshot() {
|
||||
let doc1 = loro_alpha_4::LoroDoc::new();
|
||||
doc1.set_peer_id(1).unwrap();
|
||||
gen_random_ops!(doc1, 1234, 100);
|
||||
let snapshot = doc1.export(loro_alpha_4::ExportMode::Snapshot).unwrap();
|
||||
|
||||
let doc2 = loro::LoroDoc::new();
|
||||
doc2.import(&snapshot).unwrap();
|
||||
assert_eq!(
|
||||
doc1.get_deep_value().to_json_value(),
|
||||
doc2.get_deep_value().to_json_value()
|
||||
);
|
||||
|
||||
gen_random_ops!(doc2, 5678, 100);
|
||||
let snapshot = doc2.export(loro::ExportMode::Snapshot).unwrap();
|
||||
doc1.import(&snapshot).unwrap();
|
||||
assert_eq!(
|
||||
doc1.get_deep_value().to_json_value(),
|
||||
doc2.get_deep_value().to_json_value()
|
||||
);
|
||||
|
||||
let updates =
|
||||
serde_json::to_value(doc1.export_json_updates(&Default::default(), &doc1.oplog_vv()))
|
||||
.unwrap();
|
||||
let updates_b =
|
||||
serde_json::to_value(doc2.export_json_updates(&Default::default(), &doc2.oplog_vv()))
|
||||
.unwrap();
|
||||
assert_eq!(updates, updates_b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_updates() {
|
||||
let doc1 = loro_alpha_4::LoroDoc::new();
|
||||
let doc2 = Arc::new(loro::LoroDoc::new());
|
||||
let doc2_clone = doc2.clone();
|
||||
doc1.set_peer_id(1).unwrap();
|
||||
doc1.subscribe_local_update(Box::new(move |updates| {
|
||||
doc2_clone.import(updates).unwrap();
|
||||
true
|
||||
}))
|
||||
.detach();
|
||||
|
||||
for i in 0..5 {
|
||||
gen_random_ops!(doc1, i, 10);
|
||||
assert_eq!(
|
||||
doc1.get_deep_value().to_json_value(),
|
||||
doc2.get_deep_value().to_json_value()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_in_range() {
|
||||
let doc1 = loro_alpha_4::LoroDoc::new();
|
||||
let doc2 = loro::LoroDoc::new();
|
||||
|
||||
// Generate some initial content
|
||||
gen_random_ops!(doc1, 1234, 50);
|
||||
let version = doc1.oplog_vv();
|
||||
let doc1_value = doc1.get_deep_value().to_json_value();
|
||||
gen_random_ops!(doc1, 1234, 50);
|
||||
let updates = doc1
|
||||
.export(loro_alpha_4::ExportMode::updates_till(&version))
|
||||
.unwrap();
|
||||
|
||||
doc2.import(&updates).unwrap();
|
||||
assert_eq!(doc1_value, doc2.get_deep_value().to_json_value());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_updates() {
|
||||
let doc1 = loro_alpha_4::LoroDoc::new();
|
||||
let doc2 = loro::LoroDoc::new();
|
||||
|
||||
gen_random_ops!(doc1, 0, 1000);
|
||||
let updates =
|
||||
serde_json::to_string(&doc1.export_json_updates(&Default::default(), &doc1.oplog_vv()))
|
||||
.unwrap();
|
||||
doc2.import_json_updates(updates).unwrap();
|
||||
assert_eq!(
|
||||
doc1.get_deep_value().to_json_value(),
|
||||
doc2.get_deep_value().to_json_value()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -505,7 +505,7 @@ impl TreeCacheForDiff {
|
|||
self.is_ancestor_of(maybe_ancestor, parent)
|
||||
}
|
||||
TreeParentId::Deleted | TreeParentId::Root => false,
|
||||
TreeParentId::Unexist => unreachable!(),
|
||||
TreeParentId::Unexist => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3702,6 +3702,51 @@ impl MapHandler {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn keys(&self) -> impl Iterator<Item = InternalString> + '_ {
|
||||
let mut keys: Vec<InternalString> = Vec::with_capacity(self.len());
|
||||
match &self.inner {
|
||||
MaybeDetached::Detached(m) => {
|
||||
let m = m.try_lock().unwrap();
|
||||
keys = m.value.keys().map(|x| x.as_str().into()).collect();
|
||||
}
|
||||
MaybeDetached::Attached(a) => {
|
||||
a.with_state(|state| {
|
||||
for (k, _) in state.as_map_state().unwrap().iter() {
|
||||
keys.push(k.clone());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
keys.into_iter()
|
||||
}
|
||||
|
||||
pub fn values(&self) -> impl Iterator<Item = ValueOrHandler> + '_ {
|
||||
let mut values: Vec<ValueOrHandler> = Vec::with_capacity(self.len());
|
||||
match &self.inner {
|
||||
MaybeDetached::Detached(m) => {
|
||||
let m = m.try_lock().unwrap();
|
||||
values = m.value.values().cloned().collect();
|
||||
}
|
||||
MaybeDetached::Attached(a) => {
|
||||
a.with_state(|state| {
|
||||
for (_, v) in state.as_map_state().unwrap().iter() {
|
||||
let value = match &v.value {
|
||||
Some(LoroValue::Container(container_id)) => {
|
||||
ValueOrHandler::Handler(create_handler(a, container_id.clone()))
|
||||
}
|
||||
Some(value) => ValueOrHandler::Value(value.clone()),
|
||||
None => continue,
|
||||
};
|
||||
values.push(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
values.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
@ -288,6 +288,12 @@ impl TreeHandler {
|
|||
|
||||
pub(crate) fn delete_with_txn(&self, txn: &mut Transaction, target: TreeID) -> LoroResult<()> {
|
||||
let inner = self.inner.try_attached_state()?;
|
||||
let index = match self.get_index_by_tree_id(&target) {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return Err(LoroTreeError::TreeNodeDeletedOrNotExist(target).into());
|
||||
}
|
||||
};
|
||||
txn.apply_local_op(
|
||||
inner.container_idx,
|
||||
crate::op::RawOpContent::Tree(Arc::new(TreeOp::Delete { target })),
|
||||
|
@ -295,7 +301,7 @@ impl TreeHandler {
|
|||
target,
|
||||
action: TreeExternalDiff::Delete {
|
||||
old_parent: self.get_node_parent(&target).unwrap(),
|
||||
old_index: self.get_index_by_tree_id(&target).unwrap(),
|
||||
old_index: index
|
||||
},
|
||||
}]),
|
||||
&inner.state,
|
||||
|
@ -993,4 +999,17 @@ impl TreeHandler {
|
|||
MaybeDetached::Attached(a) => a.is_deleted(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match &self.inner {
|
||||
MaybeDetached::Detached(t) => {
|
||||
let t = t.try_lock().unwrap();
|
||||
t.value.map.is_empty()
|
||||
}
|
||||
MaybeDetached::Attached(a) => a.with_state(|state| {
|
||||
let a = state.as_tree_state().unwrap();
|
||||
a.is_empty()
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1013,6 +1013,13 @@ impl TreeState {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self.children.get(&TreeParentId::Root) {
|
||||
Some(c) => c.len() == 0,
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum FractionalIndexGenResult {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#![warn(missing_docs)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
use event::{DiffEvent, Subscriber};
|
||||
use loro_common::InternalString;
|
||||
pub use loro_internal::cursor::CannotFindRelativePosition;
|
||||
use loro_internal::cursor::Cursor;
|
||||
use loro_internal::cursor::PosQueryResult;
|
||||
|
@ -1281,6 +1282,19 @@ impl LoroMap {
|
|||
pub fn clear(&self) -> LoroResult<()> {
|
||||
self.handler.clear()
|
||||
}
|
||||
|
||||
/// Get the keys of the map.
|
||||
pub fn keys(&self) -> impl Iterator<Item = InternalString> + '_ {
|
||||
self.handler.keys()
|
||||
}
|
||||
|
||||
/// Get the values of the map.
|
||||
pub fn values(&self) -> impl Iterator<Item = ValueOrContainer> + '_ {
|
||||
self.handler.values().map(|v| match v {
|
||||
ValueOrHandler::Value(v) => ValueOrContainer::Value(v),
|
||||
ValueOrHandler::Handler(c) => ValueOrContainer::Container(Container::from_handler(c)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LoroMap {
|
||||
|
@ -1920,6 +1934,13 @@ impl LoroTree {
|
|||
pub fn disable_fractional_index(&self) {
|
||||
self.handler.disable_fractional_index();
|
||||
}
|
||||
|
||||
/// Whether the tree is empty.
|
||||
///
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.handler.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LoroTree {
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
"esbuild": "^0.18.20",
|
||||
"eslint": "^8.46.0",
|
||||
"loro-crdt-old": "npm:loro-crdt@=0.16.0",
|
||||
"loro-crdt-alpha-4": "npm:loro-crdt@=1.0.0-alpha.4",
|
||||
"prettier": "^3.0.0",
|
||||
"rollup": "^3.20.1",
|
||||
"rollup-plugin-dts": "^5.3.0",
|
||||
|
|
|
@ -19,10 +19,10 @@ importers:
|
|||
devDependencies:
|
||||
vite-plugin-top-level-await:
|
||||
specifier: ^1.2.2
|
||||
version: 1.4.1(vite@4.5.3)
|
||||
version: 1.4.1(rollup@4.17.2)(vite@5.2.11)
|
||||
vite-plugin-wasm:
|
||||
specifier: ^3.1.0
|
||||
version: 3.3.0(vite@4.5.3)
|
||||
version: 3.3.0(vite@5.2.11)
|
||||
|
||||
examples/loro-quill:
|
||||
dependencies:
|
||||
|
@ -44,7 +44,7 @@ importers:
|
|||
version: 1.3.10
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^4.1.0
|
||||
version: 4.6.2(vite@4.5.3)(vue@3.4.27)
|
||||
version: 4.6.2(vite@4.5.3)(vue@3.4.27(typescript@5.4.5))
|
||||
typescript:
|
||||
specifier: ^5.2.0
|
||||
version: 5.4.5
|
||||
|
@ -53,7 +53,7 @@ importers:
|
|||
version: 4.5.3
|
||||
vite-plugin-top-level-await:
|
||||
specifier: ^1.3.0
|
||||
version: 1.4.1(vite@4.5.3)
|
||||
version: 1.4.1(rollup@4.17.2)(vite@4.5.3)
|
||||
vite-plugin-wasm:
|
||||
specifier: ^3.2.2
|
||||
version: 3.3.0(vite@4.5.3)
|
||||
|
@ -82,6 +82,9 @@ importers:
|
|||
eslint:
|
||||
specifier: ^8.46.0
|
||||
version: 8.57.0
|
||||
loro-crdt-alpha-4:
|
||||
specifier: npm:loro-crdt@=1.0.0-alpha.4
|
||||
version: loro-crdt@1.0.0-alpha.4
|
||||
loro-crdt-old:
|
||||
specifier: npm:loro-crdt@=0.16.0
|
||||
version: loro-crdt@0.16.0
|
||||
|
@ -1669,9 +1672,15 @@ packages:
|
|||
loro-crdt@0.16.0:
|
||||
resolution: {integrity: sha512-U/fwXPfe3GjE96tP97qzIjGoDOz5K/zSNPs1QZ7ZO4pqOAkjkLhBifSYZWW2v1t7ufFgUa4XqXpS1pn7Hyze2A==}
|
||||
|
||||
loro-crdt@1.0.0-alpha.4:
|
||||
resolution: {integrity: sha512-WFVf/goGbiC9wmA7GNf9Fmdwry5hJq/CcQkXjoEp4QC9OYMUGm6QqKsrI7M4q05e2l7JeTc0RU858C7FRtg7qw==}
|
||||
|
||||
loro-wasm@0.16.0:
|
||||
resolution: {integrity: sha512-2puF09ppACoUwMf9+6BkuYlogY+qZqqStq6RbYYXMhL8Uxrf8DXJdmLA+XGgI4suxXh39jvt6XfwdafqcUuu4A==}
|
||||
|
||||
loro-wasm@1.0.0-alpha.4:
|
||||
resolution: {integrity: sha512-M1Bwh1PQQUFwdAldpSGZD3wl5D/6gQkbTC6aRoC8SjIxv0mgVzibp9sw9DXnolwoJ/Abd4hWeCz2sOvYOzw+Hg==}
|
||||
|
||||
loupe@2.3.7:
|
||||
resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
|
||||
|
||||
|
@ -2896,15 +2905,19 @@ snapshots:
|
|||
is-builtin-module: 3.2.1
|
||||
is-module: 1.0.0
|
||||
resolve: 1.22.8
|
||||
optionalDependencies:
|
||||
rollup: 3.29.4
|
||||
|
||||
'@rollup/plugin-virtual@3.0.2': {}
|
||||
'@rollup/plugin-virtual@3.0.2(rollup@4.17.2)':
|
||||
optionalDependencies:
|
||||
rollup: 4.17.2
|
||||
|
||||
'@rollup/pluginutils@5.1.0(rollup@3.29.4)':
|
||||
dependencies:
|
||||
'@types/estree': 1.0.5
|
||||
estree-walker: 2.0.2
|
||||
picomatch: 2.3.1
|
||||
optionalDependencies:
|
||||
rollup: 3.29.4
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.17.2':
|
||||
|
@ -3033,6 +3046,7 @@ snapshots:
|
|||
'@typescript-eslint/visitor-keys': 6.21.0
|
||||
debug: 4.3.4
|
||||
eslint: 8.57.0
|
||||
optionalDependencies:
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
@ -3054,6 +3068,7 @@ snapshots:
|
|||
minimatch: 9.0.3
|
||||
semver: 7.6.2
|
||||
ts-api-utils: 1.3.0(typescript@5.4.5)
|
||||
optionalDependencies:
|
||||
typescript: 5.4.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
@ -3065,7 +3080,7 @@ snapshots:
|
|||
|
||||
'@ungap/structured-clone@1.2.0': {}
|
||||
|
||||
'@vitejs/plugin-vue@4.6.2(vite@4.5.3)(vue@3.4.27)':
|
||||
'@vitejs/plugin-vue@4.6.2(vite@4.5.3)(vue@3.4.27(typescript@5.4.5))':
|
||||
dependencies:
|
||||
vite: 4.5.3
|
||||
vue: 3.4.27(typescript@5.4.5)
|
||||
|
@ -3163,8 +3178,9 @@ snapshots:
|
|||
minimatch: 9.0.4
|
||||
muggle-string: 0.3.1
|
||||
path-browserify: 1.0.1
|
||||
typescript: 5.4.5
|
||||
vue-template-compiler: 2.7.16
|
||||
optionalDependencies:
|
||||
typescript: 5.4.5
|
||||
|
||||
'@vue/reactivity@3.4.27':
|
||||
dependencies:
|
||||
|
@ -3181,7 +3197,7 @@ snapshots:
|
|||
'@vue/shared': 3.4.27
|
||||
csstype: 3.1.3
|
||||
|
||||
'@vue/server-renderer@3.4.27(vue@3.4.27)':
|
||||
'@vue/server-renderer@3.4.27(vue@3.4.27(typescript@5.4.5))':
|
||||
dependencies:
|
||||
'@vue/compiler-ssr': 3.4.27
|
||||
'@vue/shared': 3.4.27
|
||||
|
@ -4159,8 +4175,14 @@ snapshots:
|
|||
dependencies:
|
||||
loro-wasm: 0.16.0
|
||||
|
||||
loro-crdt@1.0.0-alpha.4:
|
||||
dependencies:
|
||||
loro-wasm: 1.0.0-alpha.4
|
||||
|
||||
loro-wasm@0.16.0: {}
|
||||
|
||||
loro-wasm@1.0.0-alpha.4: {}
|
||||
|
||||
loupe@2.3.7:
|
||||
dependencies:
|
||||
get-func-name: 2.0.2
|
||||
|
@ -4836,9 +4858,9 @@ snapshots:
|
|||
- supports-color
|
||||
- terser
|
||||
|
||||
vite-plugin-top-level-await@1.4.1(vite@4.5.3):
|
||||
vite-plugin-top-level-await@1.4.1(rollup@4.17.2)(vite@4.5.3):
|
||||
dependencies:
|
||||
'@rollup/plugin-virtual': 3.0.2
|
||||
'@rollup/plugin-virtual': 3.0.2(rollup@4.17.2)
|
||||
'@swc/core': 1.5.5
|
||||
uuid: 9.0.1
|
||||
vite: 4.5.3
|
||||
|
@ -4846,10 +4868,24 @@ snapshots:
|
|||
- '@swc/helpers'
|
||||
- rollup
|
||||
|
||||
vite-plugin-top-level-await@1.4.1(rollup@4.17.2)(vite@5.2.11):
|
||||
dependencies:
|
||||
'@rollup/plugin-virtual': 3.0.2(rollup@4.17.2)
|
||||
'@swc/core': 1.5.5
|
||||
uuid: 9.0.1
|
||||
vite: 5.2.11
|
||||
transitivePeerDependencies:
|
||||
- '@swc/helpers'
|
||||
- rollup
|
||||
|
||||
vite-plugin-wasm@3.3.0(vite@4.5.3):
|
||||
dependencies:
|
||||
vite: 4.5.3
|
||||
|
||||
vite-plugin-wasm@3.3.0(vite@5.2.11):
|
||||
dependencies:
|
||||
vite: 5.2.11
|
||||
|
||||
vite@4.5.3:
|
||||
dependencies:
|
||||
esbuild: 0.18.20
|
||||
|
@ -4872,7 +4908,6 @@ snapshots:
|
|||
'@vitest/runner': 1.6.0
|
||||
'@vitest/snapshot': 1.6.0
|
||||
'@vitest/spy': 1.6.0
|
||||
'@vitest/ui': 1.6.0(vitest@1.6.0)
|
||||
'@vitest/utils': 1.6.0
|
||||
acorn-walk: 8.3.2
|
||||
chai: 4.4.1
|
||||
|
@ -4889,6 +4924,8 @@ snapshots:
|
|||
vite: 5.2.11
|
||||
vite-node: 1.6.0
|
||||
why-is-node-running: 2.2.2
|
||||
optionalDependencies:
|
||||
'@vitest/ui': 1.6.0(vitest@1.6.0)
|
||||
transitivePeerDependencies:
|
||||
- less
|
||||
- lightningcss
|
||||
|
@ -4915,8 +4952,9 @@ snapshots:
|
|||
'@vue/compiler-dom': 3.4.27
|
||||
'@vue/compiler-sfc': 3.4.27
|
||||
'@vue/runtime-dom': 3.4.27
|
||||
'@vue/server-renderer': 3.4.27(vue@3.4.27)
|
||||
'@vue/server-renderer': 3.4.27(vue@3.4.27(typescript@5.4.5))
|
||||
'@vue/shared': 3.4.27
|
||||
optionalDependencies:
|
||||
typescript: 5.4.5
|
||||
|
||||
wcwidth@1.0.1:
|
||||
|
|
Loading…
Reference in a new issue