use super::common::{PositionId, AdvancedCrdtError};
use super::super::{CRDT, Mergeable, ReplicaId};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct YjsNode<T> {
pub id: PositionId,
pub value: T,
pub parent: Option<PositionId>,
pub children: Vec<PositionId>,
pub visible: bool,
}
impl<T> YjsNode<T> {
pub fn new(id: PositionId, value: T, parent: Option<PositionId>) -> Self {
Self {
id,
value,
parent,
children: Vec::new(),
visible: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct YjsTreeNode<T> {
pub id: PositionId,
pub value: T,
pub children: Vec<YjsTreeNode<T>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct YjsTree<T> {
replica_id: ReplicaId,
nodes: HashMap<PositionId, YjsNode<T>>,
root: Option<PositionId>,
timestamp_counter: u64,
disambiguation_counter: u64,
}
impl<T: Clone + PartialEq> YjsTree<T> {
pub fn new(replica_id: ReplicaId) -> Self {
Self {
replica_id,
nodes: HashMap::new(),
root: None,
timestamp_counter: 0,
disambiguation_counter: 0,
}
}
pub fn add_root(&mut self, value: T) -> Result<PositionId, AdvancedCrdtError> {
self.timestamp_counter += 1;
self.disambiguation_counter += 1;
let id = PositionId::new(
self.replica_id.clone(),
self.timestamp_counter,
self.disambiguation_counter,
);
let node = YjsNode::new(id.clone(), value, None);
self.nodes.insert(id.clone(), node);
self.root = Some(id.clone());
Ok(id)
}
pub fn add_child(&mut self, parent_id: &PositionId, value: T) -> Result<PositionId, AdvancedCrdtError> {
if !self.nodes.contains_key(parent_id) {
return Err(AdvancedCrdtError::ElementNotFound(format!("Parent {:?}", parent_id)));
}
self.timestamp_counter += 1;
self.disambiguation_counter += 1;
let id = PositionId::new(
self.replica_id.clone(),
self.timestamp_counter,
self.disambiguation_counter,
);
let node = YjsNode::new(id.clone(), value, Some(parent_id.clone()));
self.nodes.insert(id.clone(), node);
if let Some(parent) = self.nodes.get_mut(parent_id) {
parent.children.push(id.clone());
}
Ok(id)
}
pub fn delete(&mut self, node_id: &PositionId) -> Result<(), AdvancedCrdtError> {
if let Some(node) = self.nodes.get_mut(node_id) {
node.visible = false;
Ok(())
} else {
Err(AdvancedCrdtError::ElementNotFound(format!("Node {:?}", node_id)))
}
}
pub fn to_tree(&self) -> Option<YjsTreeNode<T>> {
if let Some(root_id) = &self.root {
self.build_tree_node(root_id)
} else {
None
}
}
fn build_tree_node(&self, node_id: &PositionId) -> Option<YjsTreeNode<T>> {
if let Some(node) = self.nodes.get(node_id) {
if !node.visible {
return None;
}
let children: Vec<YjsTreeNode<T>> = node.children
.iter()
.filter_map(|child_id| self.build_tree_node(child_id))
.collect();
Some(YjsTreeNode {
id: node.id.clone(),
value: node.value.clone(),
children,
})
} else {
None
}
}
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
}
impl<T: Clone + PartialEq> CRDT for YjsTree<T> {
fn replica_id(&self) -> &ReplicaId {
&self.replica_id
}
}
impl<T: Clone + PartialEq + Send + Sync> Mergeable for YjsTree<T> {
type Error = AdvancedCrdtError;
fn merge(&mut self, other: &Self) -> Result<(), Self::Error> {
for (node_id, other_node) in &other.nodes {
if let Some(self_node) = self.nodes.get_mut(node_id) {
if other_node.id.timestamp > self_node.id.timestamp {
*self_node = other_node.clone();
}
} else {
self.nodes.insert(node_id.clone(), other_node.clone());
}
}
if let Some(other_root) = &other.root {
if self.root.is_none() {
self.root = Some(other_root.clone());
} else if let Some(self_root) = &self.root {
if let (Some(other_root_node), Some(self_root_node)) = (
other.nodes.get(other_root),
self.nodes.get(self_root)
) {
if other_root_node.id.timestamp > self_root_node.id.timestamp {
self.root = Some(other_root.clone());
}
}
}
}
Ok(())
}
fn has_conflict(&self, other: &Self) -> bool {
for (node_id, self_node) in &self.nodes {
if let Some(other_node) = other.nodes.get(node_id) {
if self_node.value != other_node.value {
return true;
}
}
}
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::super::ReplicaId;
use uuid::Uuid;
fn create_replica(id: u64) -> ReplicaId {
ReplicaId::from(Uuid::from_u64_pair(0, id))
}
#[test]
fn test_yjs_tree_creation() {
let replica_id = create_replica(1);
let tree = YjsTree::<String>::new(replica_id.clone());
assert_eq!(tree.replica_id(), &replica_id);
assert!(tree.is_empty());
assert_eq!(tree.len(), 0);
}
#[test]
fn test_yjs_tree_operations() {
let replica_id = create_replica(1);
let mut tree = YjsTree::<String>::new(replica_id);
let root_id = tree.add_root("root".to_string()).unwrap();
assert_eq!(tree.len(), 1);
let child1_id = tree.add_child(&root_id, "child1".to_string()).unwrap();
let child2_id = tree.add_child(&root_id, "child2".to_string()).unwrap();
assert_eq!(tree.len(), 3);
let grandchild_id = tree.add_child(&child1_id, "grandchild".to_string()).unwrap();
assert_eq!(tree.len(), 4);
tree.delete(&child2_id).unwrap();
assert_eq!(tree.len(), 4);
let tree_structure = tree.to_tree().unwrap();
assert_eq!(tree_structure.value, "root");
assert_eq!(tree_structure.children.len(), 1); assert_eq!(tree_structure.children[0].value, "child1");
assert_eq!(tree_structure.children[0].children.len(), 1);
assert_eq!(tree_structure.children[0].children[0].value, "grandchild");
}
#[test]
fn test_yjs_tree_merge() {
let replica_id1 = create_replica(1);
let replica_id2 = create_replica(2);
let mut tree1 = YjsTree::<String>::new(replica_id1);
let mut tree2 = YjsTree::<String>::new(replica_id2);
let root1_id = tree1.add_root("root1".to_string()).unwrap();
let root2_id = tree2.add_root("root2".to_string()).unwrap();
tree1.add_child(&root1_id, "child1".to_string()).unwrap();
tree2.add_child(&root2_id, "child2".to_string()).unwrap();
tree1.merge(&tree2).unwrap();
assert_eq!(tree1.len(), 4); }
}