mirror of
https://github.com/loro-dev/loro.git
synced 2025-02-06 12:25:03 +00:00
421 lines
12 KiB
Rust
421 lines
12 KiB
Rust
use std::fmt::Debug;
|
|
use std::ops::{Deref, DerefMut};
|
|
|
|
use enum_as_inner::EnumAsInner;
|
|
use fxhash::FxHashMap;
|
|
use loro::{
|
|
event::{Diff, DiffEvent, ListDiffItem},
|
|
ContainerType, Index, LoroDoc, LoroText, LoroValue, ValueOrContainer,
|
|
};
|
|
|
|
use crate::container::TreeTracker;
|
|
use loro::ContainerID;
|
|
|
|
use crate::container::CounterTracker;
|
|
|
|
#[derive(Debug, EnumAsInner)]
|
|
pub enum Value {
|
|
Value(LoroValue),
|
|
Container(ContainerTracker),
|
|
}
|
|
|
|
impl Value {
|
|
pub fn empty_container(ty: ContainerType, id: ContainerID) -> Self {
|
|
match ty {
|
|
ContainerType::Map => Value::Container(ContainerTracker::Map(MapTracker::empty(id))),
|
|
ContainerType::List => Value::Container(ContainerTracker::List(ListTracker::empty(id))),
|
|
ContainerType::MovableList => {
|
|
Value::Container(ContainerTracker::MovableList(MovableListTracker::empty(id)))
|
|
}
|
|
ContainerType::Text => Value::Container(ContainerTracker::Text(TextTracker::empty(id))),
|
|
ContainerType::Tree => Value::Container(ContainerTracker::Tree(TreeTracker::empty(id))),
|
|
ContainerType::Counter => {
|
|
Value::Container(ContainerTracker::Counter(CounterTracker::empty(id)))
|
|
}
|
|
ContainerType::Unknown(_) => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<LoroValue> for Value {
|
|
fn from(value: LoroValue) -> Self {
|
|
Value::Value(value)
|
|
}
|
|
}
|
|
|
|
impl From<ContainerTracker> for Value {
|
|
fn from(value: ContainerTracker) -> Self {
|
|
Value::Container(value)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, EnumAsInner)]
|
|
pub enum ContainerTracker {
|
|
Map(MapTracker),
|
|
List(ListTracker),
|
|
MovableList(MovableListTracker),
|
|
Text(TextTracker),
|
|
Tree(TreeTracker),
|
|
Counter(CounterTracker),
|
|
}
|
|
|
|
impl ContainerTracker {
|
|
pub fn to_value(&self) -> LoroValue {
|
|
match self {
|
|
ContainerTracker::Map(map) => map.to_value(),
|
|
ContainerTracker::List(list) => list.to_value(),
|
|
ContainerTracker::MovableList(list) => list.to_value(),
|
|
ContainerTracker::Text(text) => text.to_value(),
|
|
ContainerTracker::Tree(tree) => tree.to_value(),
|
|
ContainerTracker::Counter(counter) => counter.to_value(),
|
|
}
|
|
}
|
|
|
|
pub fn id(&self) -> &ContainerID {
|
|
match self {
|
|
ContainerTracker::Map(map) => map.id(),
|
|
ContainerTracker::List(list) => list.id(),
|
|
ContainerTracker::MovableList(list) => list.id(),
|
|
ContainerTracker::Text(text) => text.id(),
|
|
ContainerTracker::Tree(tree) => tree.id(),
|
|
ContainerTracker::Counter(counter) => counter.id(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct MapTracker {
|
|
id: ContainerID,
|
|
map: FxHashMap<String, Value>,
|
|
}
|
|
impl Deref for MapTracker {
|
|
type Target = FxHashMap<String, Value>;
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.map
|
|
}
|
|
}
|
|
impl DerefMut for MapTracker {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.map
|
|
}
|
|
}
|
|
|
|
impl ApplyDiff for MapTracker {
|
|
fn empty(id: ContainerID) -> Self {
|
|
MapTracker {
|
|
map: Default::default(),
|
|
id,
|
|
}
|
|
}
|
|
|
|
fn id(&self) -> &ContainerID {
|
|
&self.id
|
|
}
|
|
|
|
fn apply_diff(&mut self, diff: Diff) {
|
|
let diff = diff.as_map().unwrap();
|
|
for (k, v) in diff.updated.iter() {
|
|
if let Some(v) = v {
|
|
match v {
|
|
ValueOrContainer::Value(v) => {
|
|
self.insert(k.to_string(), v.clone().into());
|
|
}
|
|
ValueOrContainer::Container(c) => {
|
|
self.insert(k.to_string(), Value::empty_container(c.get_type(), c.id()));
|
|
}
|
|
}
|
|
} else {
|
|
self.remove(*k);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn to_value(&self) -> LoroValue {
|
|
let mut map = FxHashMap::default();
|
|
for (k, v) in self.iter() {
|
|
match v {
|
|
Value::Container(c) => {
|
|
map.insert(k.clone(), c.to_value());
|
|
}
|
|
Value::Value(v) => {
|
|
map.insert(k.clone(), v.clone());
|
|
}
|
|
}
|
|
}
|
|
map.into()
|
|
}
|
|
}
|
|
#[derive(Debug)]
|
|
pub struct ListTracker {
|
|
id: ContainerID,
|
|
list: Vec<Value>,
|
|
}
|
|
impl Deref for ListTracker {
|
|
type Target = Vec<Value>;
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.list
|
|
}
|
|
}
|
|
impl DerefMut for ListTracker {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.list
|
|
}
|
|
}
|
|
|
|
impl ApplyDiff for ListTracker {
|
|
fn empty(id: ContainerID) -> Self {
|
|
Self {
|
|
list: Vec::new(),
|
|
id,
|
|
}
|
|
}
|
|
|
|
fn id(&self) -> &ContainerID {
|
|
&self.id
|
|
}
|
|
|
|
fn apply_diff(&mut self, diff: Diff) {
|
|
let diff = diff.as_list().unwrap();
|
|
let mut index = 0;
|
|
for item in diff.iter() {
|
|
match item {
|
|
ListDiffItem::Retain { retain: len } => {
|
|
index += len;
|
|
}
|
|
ListDiffItem::Insert { insert: value, .. } => {
|
|
for v in value {
|
|
let value = match v {
|
|
ValueOrContainer::Container(c) => {
|
|
Value::empty_container(c.get_type(), c.id())
|
|
}
|
|
ValueOrContainer::Value(v) => Value::Value(v.clone()),
|
|
};
|
|
self.insert(index, value);
|
|
index += 1;
|
|
}
|
|
}
|
|
ListDiffItem::Delete { delete: len } => {
|
|
self.drain(index..index + *len);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn to_value(&self) -> LoroValue {
|
|
self.iter()
|
|
.map(|v| match v {
|
|
Value::Container(c) => c.to_value(),
|
|
Value::Value(v) => v.clone(),
|
|
})
|
|
.collect::<Vec<_>>()
|
|
.into()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct MovableListTracker {
|
|
list: Vec<Value>,
|
|
id: ContainerID,
|
|
}
|
|
|
|
impl Deref for MovableListTracker {
|
|
type Target = Vec<Value>;
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.list
|
|
}
|
|
}
|
|
impl DerefMut for MovableListTracker {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.list
|
|
}
|
|
}
|
|
|
|
impl ApplyDiff for MovableListTracker {
|
|
fn empty(id: ContainerID) -> Self {
|
|
Self {
|
|
list: Vec::new(),
|
|
id,
|
|
}
|
|
}
|
|
|
|
fn id(&self) -> &ContainerID {
|
|
&self.id
|
|
}
|
|
|
|
fn apply_diff(&mut self, diff: Diff) {
|
|
let diff = diff.as_list().unwrap();
|
|
let mut index = 0;
|
|
let mut maybe_from_move = FxHashMap::default();
|
|
let mut id_to_container = FxHashMap::default();
|
|
for item in diff.iter() {
|
|
match item {
|
|
ListDiffItem::Retain { retain: len } => {
|
|
index += len;
|
|
}
|
|
ListDiffItem::Insert {
|
|
insert: value,
|
|
is_move,
|
|
} => {
|
|
for v in value {
|
|
let value = match v {
|
|
ValueOrContainer::Container(c) => {
|
|
if let Some(c) = id_to_container.remove(&c.id()) {
|
|
Value::Container(c)
|
|
} else {
|
|
if *is_move {
|
|
maybe_from_move.insert(c.id().clone(), index);
|
|
}
|
|
Value::empty_container(c.get_type(), c.id())
|
|
}
|
|
}
|
|
ValueOrContainer::Value(v) => Value::Value(v.clone()),
|
|
};
|
|
self.insert(index, value);
|
|
index += 1;
|
|
}
|
|
}
|
|
ListDiffItem::Delete { delete: len } => {
|
|
for v in self.drain(index..index + *len) {
|
|
if let Value::Container(c) = v {
|
|
let id = c.id().clone();
|
|
id_to_container.insert(id, c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (id, index) in maybe_from_move {
|
|
if let Some(old) = id_to_container.remove(&id) {
|
|
self.list[index] = Value::Container(old);
|
|
} else {
|
|
// It may happen that the container is moved and also the value changed
|
|
// thus the container is not in the id_to_container map
|
|
}
|
|
}
|
|
}
|
|
|
|
fn to_value(&self) -> LoroValue {
|
|
self.iter()
|
|
.map(|v| match v {
|
|
Value::Container(c) => c.to_value(),
|
|
Value::Value(v) => v.clone(),
|
|
})
|
|
.collect::<Vec<_>>()
|
|
.into()
|
|
}
|
|
}
|
|
|
|
pub struct TextTracker {
|
|
_doc: LoroDoc,
|
|
id: ContainerID,
|
|
pub text: LoroText,
|
|
}
|
|
|
|
impl Debug for TextTracker {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("TextTracker")
|
|
.field("text", &self.text)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl ApplyDiff for TextTracker {
|
|
fn empty(id: ContainerID) -> Self {
|
|
let doc = LoroDoc::new();
|
|
let text = doc.get_text("text");
|
|
TextTracker {
|
|
_doc: doc,
|
|
text,
|
|
id,
|
|
}
|
|
}
|
|
|
|
fn id(&self) -> &ContainerID {
|
|
&self.id
|
|
}
|
|
|
|
fn apply_diff(&mut self, diff: Diff) {
|
|
let deltas = diff.as_text().unwrap();
|
|
self.text.apply_delta(deltas).unwrap();
|
|
}
|
|
|
|
fn to_value(&self) -> LoroValue {
|
|
self.text.to_string().into()
|
|
}
|
|
}
|
|
|
|
impl ContainerTracker {
|
|
pub fn apply_diff(&mut self, diff: DiffEvent) {
|
|
for diff in diff.events {
|
|
let path = diff.path;
|
|
let mut value: &mut ContainerTracker = self;
|
|
for (_, index) in path {
|
|
match index {
|
|
Index::Key(key) => {
|
|
value = value
|
|
.as_map_mut()
|
|
.unwrap()
|
|
.get_mut(&key.to_string())
|
|
.unwrap()
|
|
.as_container_mut()
|
|
.unwrap()
|
|
}
|
|
Index::Node(tree_id) => {
|
|
value = &mut value
|
|
.as_tree_mut()
|
|
.unwrap()
|
|
.find_node_by_id_mut(*tree_id)
|
|
.unwrap()
|
|
.meta
|
|
}
|
|
Index::Seq(idx) => {
|
|
value = match value {
|
|
ContainerTracker::List(l) => {
|
|
l.get_mut(*idx).unwrap().as_container_mut().unwrap()
|
|
}
|
|
ContainerTracker::MovableList(l) => {
|
|
let item = l.get_mut(*idx).unwrap();
|
|
|
|
item.as_container_mut().unwrap()
|
|
}
|
|
_ => {
|
|
unreachable!()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
let target = diff.target;
|
|
let diff = diff.diff;
|
|
match target.container_type() {
|
|
ContainerType::Map => {
|
|
value.as_map_mut().unwrap().apply_diff(diff);
|
|
}
|
|
ContainerType::List => {
|
|
value.as_list_mut().unwrap().apply_diff(diff);
|
|
}
|
|
ContainerType::MovableList => {
|
|
value.as_movable_list_mut().unwrap().apply_diff(diff);
|
|
}
|
|
ContainerType::Text => {
|
|
value.as_text_mut().unwrap().apply_diff(diff);
|
|
}
|
|
ContainerType::Tree => {
|
|
value.as_tree_mut().unwrap().apply_diff(diff);
|
|
}
|
|
ContainerType::Counter => {
|
|
value.as_counter_mut().unwrap().apply_diff(diff);
|
|
}
|
|
ContainerType::Unknown(_) => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait ApplyDiff {
|
|
fn empty(id: ContainerID) -> Self;
|
|
fn id(&self) -> &ContainerID;
|
|
fn apply_diff(&mut self, diff: Diff);
|
|
fn to_value(&self) -> LoroValue;
|
|
}
|