mirror of
https://github.com/loro-dev/loro.git
synced 2025-01-23 05:24:51 +00:00
fix: pass delta test
This commit is contained in:
parent
0f2333b182
commit
344bbb1e34
2 changed files with 312 additions and 63 deletions
|
@ -25,14 +25,24 @@ pub enum DeltaItem<Value, Meta> {
|
|||
Delete(usize),
|
||||
}
|
||||
|
||||
pub trait Meta: Debug + Default + Clone + PartialEq {
|
||||
pub trait Meta: Debug + Clone + PartialEq {
|
||||
fn none() -> Self;
|
||||
fn empty() -> Self;
|
||||
fn is_empty(&self) -> bool;
|
||||
fn is_meta_none(&self) -> bool;
|
||||
fn compose(self, other: Self) -> Self;
|
||||
}
|
||||
|
||||
impl Meta for () {
|
||||
fn none() -> Self {}
|
||||
fn empty() -> Self {}
|
||||
fn is_empty(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn is_meta_none(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn compose(self, _other: Self) -> Self {}
|
||||
}
|
||||
|
||||
pub trait DeltaValue: Debug + HasLength + Sliceable + Clone + PartialEq {
|
||||
|
@ -40,7 +50,7 @@ pub trait DeltaValue: Debug + HasLength + Sliceable + Clone + PartialEq {
|
|||
}
|
||||
|
||||
impl<Value: DeltaValue, M: Meta> DeltaItem<Value, M> {
|
||||
pub fn meta(&self) -> Option<&M> {
|
||||
pub fn get_meta(&self) -> Option<&M> {
|
||||
match self {
|
||||
DeltaItem::Insert { meta, .. } => Some(meta),
|
||||
DeltaItem::Retain { meta, .. } => Some(meta),
|
||||
|
@ -48,6 +58,14 @@ impl<Value: DeltaValue, M: Meta> DeltaItem<Value, M> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn meta(&mut self, meta: M) {
|
||||
match self {
|
||||
DeltaItem::Insert { meta: m, .. } => *m = meta,
|
||||
DeltaItem::Retain { meta: m, .. } => *m = meta,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_retain(&self) -> bool {
|
||||
matches!(self, Self::Retain { .. })
|
||||
}
|
||||
|
@ -102,26 +120,27 @@ impl<V: DeltaValue, M: Meta> DeltaIterator<V, M> {
|
|||
if next_op.is_none() {
|
||||
return DeltaItem::Retain {
|
||||
len: usize::MAX,
|
||||
meta: Default::default(),
|
||||
meta: M::none(),
|
||||
};
|
||||
}
|
||||
}
|
||||
// TODO: Maybe can avoid cloning
|
||||
let op = self.peek().unwrap().clone();
|
||||
let op = self.peek().unwrap();
|
||||
let op_length = op.content_len();
|
||||
let offset = self.offset;
|
||||
if length >= op_length - offset {
|
||||
length = op_length - offset;
|
||||
self.index += 1;
|
||||
self.offset = 0;
|
||||
} else {
|
||||
self.offset += length;
|
||||
}
|
||||
let (index_delta, offset_delta) = {
|
||||
if length >= op_length - offset {
|
||||
length = op_length - offset;
|
||||
(1, -(offset as isize))
|
||||
} else {
|
||||
(0, length as isize)
|
||||
}
|
||||
};
|
||||
|
||||
if op.is_delete() {
|
||||
let ans = if op.is_delete() {
|
||||
DeltaItem::Delete(length)
|
||||
} else {
|
||||
let mut ans_op = op;
|
||||
let mut ans_op = op.clone();
|
||||
if ans_op.is_retain() {
|
||||
*ans_op.as_retain_mut().unwrap().0 = length;
|
||||
} else if ans_op.is_insert() {
|
||||
|
@ -129,7 +148,11 @@ impl<V: DeltaValue, M: Meta> DeltaIterator<V, M> {
|
|||
*v = v.slice(offset, offset + length);
|
||||
}
|
||||
ans_op
|
||||
}
|
||||
};
|
||||
|
||||
self.index += index_delta;
|
||||
self.offset = (self.offset as isize + offset_delta) as usize;
|
||||
ans
|
||||
}
|
||||
|
||||
fn rest(&mut self) -> SmallVec<[DeltaItem<V, M>; 2]> {
|
||||
|
@ -212,12 +235,15 @@ impl<Value: DeltaValue, M: Meta> SeqDelta<Value, M> {
|
|||
}
|
||||
|
||||
pub fn retain_with_meta(mut self, len: usize, meta: M) -> Self {
|
||||
self.vec.push(DeltaItem::Retain { len, meta });
|
||||
self.push(DeltaItem::Retain { len, meta });
|
||||
self
|
||||
}
|
||||
|
||||
pub fn insert_with_meta(mut self, value: Value, meta: M) -> Self {
|
||||
self.vec.push(DeltaItem::Insert { value, meta });
|
||||
pub fn insert_with_meta<V: Into<Value>>(mut self, value: V, meta: M) -> Self {
|
||||
self.push(DeltaItem::Insert {
|
||||
value: value.into(),
|
||||
meta,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -225,7 +251,26 @@ impl<Value: DeltaValue, M: Meta> SeqDelta<Value, M> {
|
|||
if len == 0 {
|
||||
return self;
|
||||
}
|
||||
self.vec.push(DeltaItem::Delete(len));
|
||||
self.push(DeltaItem::Delete(len));
|
||||
self
|
||||
}
|
||||
pub fn retain(mut self, len: usize) -> Self {
|
||||
if len == 0 {
|
||||
return self;
|
||||
}
|
||||
|
||||
self.push(DeltaItem::Retain {
|
||||
len,
|
||||
meta: M::none(),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn insert<V: Into<Value>>(mut self, value: V) -> Self {
|
||||
self.push(DeltaItem::Insert {
|
||||
value: value.into(),
|
||||
meta: M::none(),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -242,6 +287,10 @@ impl<Value: DeltaValue, M: Meta> SeqDelta<Value, M> {
|
|||
// always prefer to insert first
|
||||
if last_op.is_delete() && new_op.is_insert() {
|
||||
index -= 1;
|
||||
if index == 0 {
|
||||
self.vec.insert(0, new_op);
|
||||
return;
|
||||
}
|
||||
let _last_op = self.vec.get_mut(index - 1);
|
||||
if let Some(_last_op_inner) = _last_op {
|
||||
last_op = _last_op_inner;
|
||||
|
@ -250,20 +299,20 @@ impl<Value: DeltaValue, M: Meta> SeqDelta<Value, M> {
|
|||
return;
|
||||
}
|
||||
}
|
||||
if new_op.meta() == last_op.meta() {
|
||||
if new_op.get_meta() == last_op.get_meta() {
|
||||
if new_op.is_insert() && last_op.is_insert() {
|
||||
// TODO avoid cloning
|
||||
let mut value = last_op.as_insert_mut().unwrap().0.clone();
|
||||
value.extend(new_op.as_insert().unwrap().0.clone());
|
||||
self.vec[index - 1] = DeltaItem::Insert {
|
||||
value,
|
||||
meta: new_op.meta().unwrap().clone(),
|
||||
meta: new_op.get_meta().unwrap().clone(),
|
||||
};
|
||||
return;
|
||||
} else if new_op.is_retain() && last_op.is_retain() {
|
||||
self.vec[index - 1] = DeltaItem::Retain {
|
||||
len: last_op.content_len() + new_op.content_len(),
|
||||
meta: new_op.meta().unwrap().clone(),
|
||||
meta: new_op.get_meta().unwrap().clone(),
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
@ -303,9 +352,7 @@ impl<Value: DeltaValue, M: Meta> SeqDelta<Value, M> {
|
|||
let mut ops = smallvec![];
|
||||
let first_other = other_iter.peek();
|
||||
if let Some(first_other) = first_other {
|
||||
if first_other.is_retain()
|
||||
&& (first_other.meta().is_none() || first_other.meta().unwrap().is_empty())
|
||||
{
|
||||
if first_other.is_retain() && first_other.get_meta().is_none() {
|
||||
let mut first_left = first_other.content_len();
|
||||
let mut first_this = this_iter.peek();
|
||||
while let Some(first_this_inner) = first_this {
|
||||
|
@ -334,29 +381,31 @@ impl<Value: DeltaValue, M: Meta> SeqDelta<Value, M> {
|
|||
let this_op = this_iter.next(length);
|
||||
let other_op = other_iter.next(length);
|
||||
if other_op.is_retain() {
|
||||
let new_op = if this_op.is_retain() {
|
||||
let mut new_op = if this_op.is_retain() {
|
||||
DeltaItem::Retain {
|
||||
len: length,
|
||||
meta: M::default(),
|
||||
meta: M::none(),
|
||||
}
|
||||
} else {
|
||||
this_op.clone()
|
||||
};
|
||||
// TODO: Meta compose
|
||||
let meta = this_op
|
||||
.get_meta()
|
||||
.unwrap()
|
||||
.clone()
|
||||
.compose(other_op.get_meta().unwrap().clone());
|
||||
new_op.meta(meta);
|
||||
delta.push(new_op.clone());
|
||||
if !other_iter.has_next() && delta.vec[delta.vec.len() - 1].eq(&new_op) {
|
||||
let rest = SeqDelta {
|
||||
vec: this_iter.rest(),
|
||||
};
|
||||
if !other_iter.has_next() && delta.vec.last().unwrap().eq(&new_op) {
|
||||
let vec = this_iter.rest();
|
||||
if vec.is_empty() {
|
||||
return delta.chop();
|
||||
}
|
||||
let rest = SeqDelta { vec };
|
||||
return delta.concat(rest).chop();
|
||||
}
|
||||
} else if other_op.is_delete() {
|
||||
if this_op.is_retain() {
|
||||
delta.push(other_op);
|
||||
} else {
|
||||
// this op is insert
|
||||
continue;
|
||||
}
|
||||
} else if other_op.is_delete() && this_op.is_retain() {
|
||||
delta.push(other_op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -379,7 +428,7 @@ impl<Value: DeltaValue, M: Meta> SeqDelta<Value, M> {
|
|||
fn chop(mut self) -> Self {
|
||||
let last_op = self.vec.last();
|
||||
if let Some(last_op) = last_op {
|
||||
if last_op.is_retain() && last_op.meta().unwrap().is_empty() {
|
||||
if last_op.is_retain() && last_op.get_meta().unwrap().is_meta_none() {
|
||||
self.vec.pop();
|
||||
}
|
||||
}
|
||||
|
@ -393,28 +442,6 @@ impl<Value: DeltaValue, M: Meta> Default for SeqDelta<Value, M> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Value: HasLength, M: Default + Meta> SeqDelta<Value, M> {
|
||||
pub fn retain(mut self, len: usize) -> Self {
|
||||
if len == 0 {
|
||||
return self;
|
||||
}
|
||||
|
||||
self.vec.push(DeltaItem::Retain {
|
||||
len,
|
||||
meta: Default::default(),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn insert(mut self, value: Value) -> Self {
|
||||
self.vec.push(DeltaItem::Insert {
|
||||
value,
|
||||
meta: Default::default(),
|
||||
});
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + PartialEq + Debug> DeltaValue for Vec<T> {
|
||||
fn extend(&mut self, other: Self) {
|
||||
<Vec<_> as std::iter::Extend<_>>::extend(self, other)
|
||||
|
@ -429,7 +456,65 @@ impl DeltaValue for String {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{DeltaItem, SeqDelta};
|
||||
use super::{DeltaItem, Meta, SeqDelta};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Default)]
|
||||
struct M {
|
||||
bold: Option<bool>,
|
||||
color: Option<()>,
|
||||
}
|
||||
|
||||
type TestMeta = Option<M>;
|
||||
|
||||
impl Meta for TestMeta {
|
||||
fn none() -> Self {
|
||||
None
|
||||
}
|
||||
|
||||
fn is_meta_none(&self) -> bool {
|
||||
self.is_none()
|
||||
}
|
||||
|
||||
fn empty() -> Self {
|
||||
Some(M {
|
||||
bold: None,
|
||||
color: None,
|
||||
})
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_some()
|
||||
&& self.as_ref().unwrap().bold.is_none()
|
||||
&& self.as_ref().unwrap().color.is_none()
|
||||
}
|
||||
|
||||
fn compose(self, other: Self) -> Self {
|
||||
if other.is_empty() {
|
||||
None
|
||||
} else if other.is_none() {
|
||||
self
|
||||
} else {
|
||||
other
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type TestDelta = SeqDelta<String, TestMeta>;
|
||||
const BOLD_META: TestMeta = Some(M {
|
||||
bold: Some(true),
|
||||
color: None,
|
||||
});
|
||||
const COLOR_META: TestMeta = Some(M {
|
||||
bold: None,
|
||||
color: Some(()),
|
||||
});
|
||||
const EMPTY_META: TestMeta = Some(M {
|
||||
bold: None,
|
||||
color: None,
|
||||
});
|
||||
// const ALL_META: TestMeta = Some(M {
|
||||
// bold: Some(true),
|
||||
// color: Some(()),
|
||||
// });
|
||||
|
||||
#[test]
|
||||
fn delta_push() {
|
||||
|
@ -454,4 +539,168 @@ mod test {
|
|||
let b = SeqDelta::new().retain(1).insert("123".to_string());
|
||||
assert_eq!(a.compose(b), SeqDelta::new().insert("112323".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_insert() {
|
||||
let a = TestDelta::new().insert("a");
|
||||
let b = TestDelta::new().insert("b");
|
||||
assert_eq!(a.compose(b), TestDelta::new().insert("b").insert("a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_retain() {
|
||||
let a = TestDelta::new().insert("a");
|
||||
let b = TestDelta::new().retain_with_meta(1, BOLD_META);
|
||||
assert_eq!(
|
||||
a.compose(b),
|
||||
TestDelta::new().insert_with_meta("a", BOLD_META)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_delete() {
|
||||
let a = TestDelta::new().insert("a");
|
||||
let b = TestDelta::new().delete(1);
|
||||
assert_eq!(a.compose(b), TestDelta::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_insert() {
|
||||
let a = TestDelta::new().delete(1);
|
||||
let b = TestDelta::new().insert("b");
|
||||
assert_eq!(a.compose(b), TestDelta::new().insert("b").delete(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_retain() {
|
||||
let a = TestDelta::new().delete(1);
|
||||
let b = TestDelta::new().retain_with_meta(1, BOLD_META);
|
||||
assert_eq!(
|
||||
a.compose(b),
|
||||
TestDelta::new().delete(1).retain_with_meta(1, BOLD_META)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_delete() {
|
||||
let a = TestDelta::new().delete(1);
|
||||
let b = TestDelta::new().delete(1);
|
||||
assert_eq!(a.compose(b), TestDelta::new().delete(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retain_insert() {
|
||||
let a = TestDelta::new().retain_with_meta(1, BOLD_META);
|
||||
let b = TestDelta::new().insert("b");
|
||||
assert_eq!(
|
||||
a.compose(b),
|
||||
TestDelta::new().insert("b").retain_with_meta(1, BOLD_META)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retain_retain() {
|
||||
let a = TestDelta::new().retain_with_meta(1, BOLD_META);
|
||||
let b = TestDelta::new().retain_with_meta(1, COLOR_META);
|
||||
assert_eq!(
|
||||
a.compose(b),
|
||||
TestDelta::new().retain_with_meta(1, COLOR_META)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retain_delete() {
|
||||
let a = TestDelta::new().retain_with_meta(1, BOLD_META);
|
||||
let b = TestDelta::new().delete(1);
|
||||
assert_eq!(a.compose(b), TestDelta::new().delete(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_entire() {
|
||||
let a = TestDelta::new().retain(4).insert("abcde");
|
||||
let b = TestDelta::new().delete(9);
|
||||
assert_eq!(a.compose(b), TestDelta::new().delete(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retain_more() {
|
||||
let a = TestDelta::new().insert("abcde");
|
||||
let b = TestDelta::new().retain(10);
|
||||
assert_eq!(a.compose(b), TestDelta::new().insert("abcde"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_meta() {
|
||||
let a = TestDelta::new().insert_with_meta("a", BOLD_META);
|
||||
let b = TestDelta::new().retain_with_meta(1, EMPTY_META);
|
||||
assert_eq!(a.compose(b), TestDelta::new().insert("a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retain_start_opt() {
|
||||
let a = TestDelta::new()
|
||||
.insert_with_meta("a", BOLD_META)
|
||||
.insert("b")
|
||||
.insert_with_meta("c", BOLD_META)
|
||||
.delete(1);
|
||||
let b = TestDelta::new().retain(3).insert("d");
|
||||
let expect = TestDelta::new()
|
||||
.insert_with_meta("a", BOLD_META)
|
||||
.insert("b")
|
||||
.insert_with_meta("c", BOLD_META)
|
||||
.insert("d")
|
||||
.delete(1);
|
||||
assert_eq!(a.compose(b), expect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retain_start_opt_split() {
|
||||
let a = TestDelta::new()
|
||||
.insert_with_meta("a", BOLD_META)
|
||||
.insert("b")
|
||||
.insert_with_meta("c", BOLD_META)
|
||||
.retain(5)
|
||||
.delete(1);
|
||||
let b = TestDelta::new().retain(4).insert("d");
|
||||
let expect = TestDelta::new()
|
||||
.insert_with_meta("a", BOLD_META)
|
||||
.insert("b")
|
||||
.insert_with_meta("c", BOLD_META)
|
||||
.retain(1)
|
||||
.insert("d")
|
||||
.retain(4)
|
||||
.delete(1);
|
||||
assert_eq!(a.compose(b), expect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retain_end_opt() {
|
||||
let a = TestDelta::new()
|
||||
.insert_with_meta("a", BOLD_META)
|
||||
.insert("b")
|
||||
.insert_with_meta("c", BOLD_META);
|
||||
let b = TestDelta::new().delete(1);
|
||||
let expect = TestDelta::new()
|
||||
.insert("b")
|
||||
.insert_with_meta("c", BOLD_META);
|
||||
assert_eq!(a.compose(b), expect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retain_end_opt_join() {
|
||||
let a = TestDelta::new()
|
||||
.insert_with_meta("a", BOLD_META)
|
||||
.insert("b")
|
||||
.insert_with_meta("c", BOLD_META)
|
||||
.insert("d")
|
||||
.insert_with_meta("e", BOLD_META)
|
||||
.insert("f");
|
||||
let b = TestDelta::new().retain(1).delete(1);
|
||||
let expect = TestDelta::new()
|
||||
.insert_with_meta("ac", BOLD_META)
|
||||
.insert("d")
|
||||
.insert_with_meta("e", BOLD_META)
|
||||
.insert("f");
|
||||
assert_eq!(a.compose(b), expect);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,7 +187,7 @@ impl TransactionOp {
|
|||
container,
|
||||
ops: SeqDelta::new()
|
||||
.retain(pos)
|
||||
.insert(values.into_iter().map(|v| v.into()).collect()),
|
||||
.insert(values.into_iter().map(Value::Value).collect::<Vec<_>>()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue