Преглед изворни кода

chore: customize node and operation serde

nathan пре 2 година
родитељ
комит
b7f65ff11d

+ 1 - 0
shared-lib/lib-ot/src/core/document/mod.rs

@@ -1,6 +1,7 @@
 #![allow(clippy::module_inception)]
 mod attributes;
 mod node;
+mod node_serde;
 mod node_tree;
 mod operation;
 mod operation_serde;

+ 28 - 7
shared-lib/lib-ot/src/core/document/node.rs

@@ -1,6 +1,8 @@
+use super::node_serde::*;
 use crate::core::NodeBody::Delta;
-use crate::core::{AttributeKey, AttributeValue, NodeAttributes, OperationTransform, TextDelta};
+use crate::core::{AttributeKey, AttributeValue, NodeAttributes, OperationTransform};
 use crate::errors::OTError;
+use crate::rich_text::RichTextDelta;
 use serde::{Deserialize, Serialize};
 
 #[derive(Default, Clone, Serialize, Deserialize, Eq, PartialEq)]
@@ -9,12 +11,17 @@ pub struct NodeData {
     pub node_type: String,
 
     #[serde(skip_serializing_if = "NodeAttributes::is_empty")]
+    #[serde(default)]
     pub attributes: NodeAttributes,
 
+    #[serde(serialize_with = "serialize_body")]
+    #[serde(deserialize_with = "deserialize_body")]
     #[serde(skip_serializing_if = "NodeBody::is_empty")]
+    #[serde(default)]
     pub body: NodeBody,
 
     #[serde(skip_serializing_if = "Vec::is_empty")]
+    #[serde(default)]
     pub children: Vec<NodeData>,
 }
 
@@ -25,14 +32,24 @@ impl NodeData {
             ..Default::default()
         }
     }
+
+    pub fn split(self) -> (Node, Vec<NodeData>) {
+        let node = Node {
+            node_type: self.node_type,
+            body: self.body,
+            attributes: self.attributes,
+        };
+
+        (node, self.children)
+    }
 }
 
 /// Builder for [`NodeData`]
-pub struct NodeBuilder {
+pub struct NodeDataBuilder {
     node: NodeData,
 }
 
-impl NodeBuilder {
+impl NodeDataBuilder {
     pub fn new<T: ToString>(node_type: T) -> Self {
         Self {
             node: NodeData::new(node_type.to_string()),
@@ -73,10 +90,10 @@ impl NodeBuilder {
 /// The NodeBody implements the [`OperationTransform`] trait which means it can perform
 /// compose, transform and invert.
 ///
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub enum NodeBody {
     Empty,
-    Delta(TextDelta),
+    Delta(RichTextDelta),
 }
 
 impl std::default::Default for NodeBody {
@@ -142,8 +159,12 @@ impl OperationTransform for NodeBody {
 ///
 /// Each NodeBody except the Empty should have its corresponding changeset variant.
 #[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "snake_case")]
 pub enum NodeBodyChangeset {
-    Delta { delta: TextDelta, inverted: TextDelta },
+    Delta {
+        delta: RichTextDelta,
+        inverted: RichTextDelta,
+    },
 }
 
 impl NodeBodyChangeset {
@@ -175,7 +196,7 @@ impl Node {
         }
     }
 
-    pub fn apply_body_changeset(&mut self, changeset: &NodeBodyChangeset) {
+    pub fn apply_body_changeset(&mut self, changeset: NodeBodyChangeset) {
         match changeset {
             NodeBodyChangeset::Delta { delta, inverted: _ } => match self.body.compose(&Delta(delta.clone())) {
                 Ok(new_body) => self.body = new_body,

+ 75 - 0
shared-lib/lib-ot/src/core/document/node_serde.rs

@@ -0,0 +1,75 @@
+use super::NodeBody;
+use crate::rich_text::RichTextDelta;
+use serde::de::{self, Visitor};
+use serde::ser::SerializeMap;
+use serde::{Deserializer, Serializer};
+use std::fmt;
+
+pub fn serialize_body<S>(body: &NodeBody, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    let mut map = serializer.serialize_map(Some(3))?;
+    match body {
+        NodeBody::Empty => {}
+        NodeBody::Delta(delta) => {
+            map.serialize_key("delta")?;
+            map.serialize_value(delta)?;
+        }
+    }
+    map.end()
+}
+
+pub fn deserialize_body<'de, D>(deserializer: D) -> Result<NodeBody, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    struct NodeBodyVisitor();
+
+    impl<'de> Visitor<'de> for NodeBodyVisitor {
+        type Value = NodeBody;
+
+        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+            formatter.write_str("Expect NodeBody")
+        }
+
+        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
+        where
+            A: de::SeqAccess<'de>,
+        {
+            let mut delta = RichTextDelta::default();
+            while let Some(op) = seq.next_element()? {
+                delta.add(op);
+            }
+            Ok(NodeBody::Delta(delta))
+        }
+
+        // #[inline]
+        // fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
+        // where
+        //     V: MapAccess<'de>,
+        // {
+        //     let mut delta: Option<RichTextDelta> = None;
+        //     while let Some(key) = map.next_key()? {
+        //         match key {
+        //             "delta" => {
+        //                 if delta.is_some() {
+        //                     return Err(de::Error::duplicate_field("delta"));
+        //                 }
+        //                 delta = Some(map.next_value()?);
+        //             }
+        //             other => {
+        //                 panic!("Unexpected key: {}", other);
+        //             }
+        //         }
+        //     }
+
+        //     if delta.is_some() {
+        //         return Ok(NodeBody::Delta(delta.unwrap()));
+        //     }
+
+        //     Err(de::Error::missing_field("delta"))
+        // }
+    }
+    deserializer.deserialize_any(NodeBodyVisitor())
+}

+ 52 - 39
shared-lib/lib-ot/src/core/document/node_tree.rs

@@ -3,6 +3,8 @@ use crate::core::{Node, NodeAttributes, NodeBodyChangeset, NodeData, NodeOperati
 use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
 use indextree::{Arena, Children, FollowingSiblings, NodeId};
 
+use super::NodeOperationList;
+
 ///
 pub struct NodeTree {
     arena: Arena<Node>,
@@ -11,17 +13,31 @@ pub struct NodeTree {
 
 impl Default for NodeTree {
     fn default() -> Self {
-        Self::new()
+        Self::new("root")
     }
 }
 
 impl NodeTree {
-    pub fn new() -> NodeTree {
+    pub fn new(root_name: &str) -> NodeTree {
         let mut arena = Arena::new();
-        let root = arena.new_node(Node::new("root"));
+        let root = arena.new_node(Node::new(root_name));
         NodeTree { arena, root }
     }
 
+    pub fn from_bytes(root_name: &str, bytes: Vec<u8>) -> Result<Self, OTError> {
+        let operations = NodeOperationList::from_bytes(bytes)?.into_inner();
+        Self::from_operations(root_name, operations)
+    }
+
+    pub fn from_operations(root_name: &str, operations: Vec<NodeOperation>) -> Result<Self, OTError> {
+        let mut node_tree = NodeTree::new(root_name);
+
+        for operation in operations {
+            let _ = node_tree.apply_op(operation)?;
+        }
+        Ok(node_tree)
+    }
+
     pub fn get_node(&self, node_id: NodeId) -> Option<&Node> {
         Some(self.arena.get(node_id)?.get())
     }
@@ -42,10 +58,10 @@ impl NodeTree {
     /// let root_path: Path = vec![0].into();
     /// let op = NodeOperation::Insert {path: root_path.clone(),nodes };
     ///
-    /// let mut node_tree = NodeTree::new();
-    /// node_tree.apply_op(&op).unwrap();
-    /// let node_id = node_tree.node_at_path(&root_path).unwrap();
-    /// let node_path = node_tree.path_of_node(node_id);
+    /// let mut node_tree = NodeTree::new("root");
+    /// node_tree.apply_op(op).unwrap();
+    /// let node_id = node_tree.node_id_at_path(&root_path).unwrap();
+    /// let node_path = node_tree.path_from_node_id(node_id);
     /// debug_assert_eq!(node_path, root_path);
     /// ```
     pub fn node_id_at_path<T: Into<Path>>(&self, path: T) -> Option<NodeId> {
@@ -100,15 +116,14 @@ impl NodeTree {
     ///
     /// ```
     /// use lib_ot::core::{NodeOperation, NodeTree, NodeData, Path};
-    /// let node = NodeData::new("text".to_string());
+    /// let node_1 = NodeData::new("text".to_string());
     /// let inserted_path: Path = vec![0].into();
     ///
-    /// let mut node_tree = NodeTree::new();
-    /// node_tree.apply_op(&NodeOperation::Insert {path: inserted_path.clone(),nodes: vec![node.clone()] }).unwrap();
+    /// let mut node_tree = NodeTree::new("root");
+    /// node_tree.apply_op(NodeOperation::Insert {path: inserted_path.clone(),nodes: vec![node_1.clone()] }).unwrap();
     ///
-    /// let inserted_note = node_tree.node_at_path(&inserted_path).unwrap();
-    /// let inserted_data = node_tree.get_node(inserted_note).unwrap();
-    /// assert_eq!(inserted_data.node_type, node.node_type);
+    /// let node_2 = node_tree.get_node_at_path(&inserted_path).unwrap();
+    /// assert_eq!(node_2.node_type, node_1.node_type);
     /// ```
     pub fn child_from_node_at_index(&self, node_id: NodeId, index: usize) -> Option<NodeId> {
         let children = node_id.children(&self.arena);
@@ -144,25 +159,26 @@ impl NodeTree {
     }
 
     pub fn apply(&mut self, transaction: Transaction) -> Result<(), OTError> {
-        for op in transaction.operations.iter() {
-            self.apply_op(op)?;
+        let operations = transaction.into_operations();
+        for operation in operations {
+            self.apply_op(operation)?;
         }
         Ok(())
     }
 
-    pub fn apply_op(&mut self, op: &NodeOperation) -> Result<(), OTError> {
+    pub fn apply_op(&mut self, op: NodeOperation) -> Result<(), OTError> {
         match op {
-            NodeOperation::Insert { path, nodes } => self.insert_nodes(path, nodes),
-            NodeOperation::UpdateAttributes { path, attributes, .. } => self.update_attributes(path, attributes),
-            NodeOperation::UpdateBody { path, changeset } => self.update_body(path, changeset),
-            NodeOperation::Delete { path, nodes } => self.delete_node(path, nodes),
+            NodeOperation::Insert { path, nodes } => self.insert_nodes(&path, nodes),
+            NodeOperation::UpdateAttributes { path, attributes, .. } => self.update_attributes(&path, attributes),
+            NodeOperation::UpdateBody { path, changeset } => self.update_body(&path, changeset),
+            NodeOperation::Delete { path, nodes } => self.delete_node(&path, nodes),
         }
     }
     /// Inserts nodes at given path
     ///
     /// returns error if the path is empty
     ///
-    fn insert_nodes(&mut self, path: &Path, nodes: &[NodeData]) -> Result<(), OTError> {
+    fn insert_nodes(&mut self, path: &Path, nodes: Vec<NodeData>) -> Result<(), OTError> {
         debug_assert!(!path.is_empty());
         if path.is_empty() {
             return Err(OTErrorCode::PathIsEmpty.into());
@@ -179,8 +195,9 @@ impl NodeTree {
 
     /// Inserts nodes before the node with node_id
     ///
-    fn insert_nodes_before(&mut self, node_id: &NodeId, nodes: &[NodeData]) {
+    fn insert_nodes_before(&mut self, node_id: &NodeId, nodes: Vec<NodeData>) {
         for node in nodes {
+            let (node, children) = node.split();
             let new_node_id = self.arena.new_node(node.into());
             if node_id.is_removed(&self.arena) {
                 tracing::warn!("Node:{:?} is remove before insert", node_id);
@@ -188,23 +205,18 @@ impl NodeTree {
             }
 
             node_id.insert_before(new_node_id, &mut self.arena);
-            self.append_nodes(&new_node_id, &node.children);
+            self.append_nodes(&new_node_id, children);
         }
     }
 
-    fn insert_nodes_at_index(
-        &mut self,
-        parent: NodeId,
-        index: usize,
-        insert_children: &[NodeData],
-    ) -> Result<(), OTError> {
+    fn insert_nodes_at_index(&mut self, parent: NodeId, index: usize, nodes: Vec<NodeData>) -> Result<(), OTError> {
         if index == 0 && parent.children(&self.arena).next().is_none() {
-            self.append_nodes(&parent, insert_children);
+            self.append_nodes(&parent, nodes);
             return Ok(());
         }
 
         if index == parent.children(&self.arena).count() {
-            self.append_nodes(&parent, insert_children);
+            self.append_nodes(&parent, nodes);
             return Ok(());
         }
 
@@ -212,28 +224,29 @@ impl NodeTree {
             .child_from_node_at_index(parent, index)
             .ok_or_else(|| ErrorBuilder::new(OTErrorCode::PathNotFound).build())?;
 
-        self.insert_nodes_before(&node_to_insert, insert_children);
+        self.insert_nodes_before(&node_to_insert, nodes);
         Ok(())
     }
 
-    fn append_nodes(&mut self, parent: &NodeId, nodes: &[NodeData]) {
+    fn append_nodes(&mut self, parent: &NodeId, nodes: Vec<NodeData>) {
         for node in nodes {
+            let (node, children) = node.split();
             let new_node_id = self.arena.new_node(node.into());
             parent.append(new_node_id, &mut self.arena);
 
-            self.append_nodes(&new_node_id, &node.children);
+            self.append_nodes(&new_node_id, children);
         }
     }
 
-    fn update_attributes(&mut self, path: &Path, attributes: &NodeAttributes) -> Result<(), OTError> {
+    fn update_attributes(&mut self, path: &Path, attributes: NodeAttributes) -> Result<(), OTError> {
         self.mut_node_at_path(path, |node| {
-            let new_attributes = NodeAttributes::compose(&node.attributes, attributes)?;
+            let new_attributes = NodeAttributes::compose(&node.attributes, &attributes)?;
             node.attributes = new_attributes;
             Ok(())
         })
     }
 
-    fn delete_node(&mut self, path: &Path, nodes: &[NodeData]) -> Result<(), OTError> {
+    fn delete_node(&mut self, path: &Path, nodes: Vec<NodeData>) -> Result<(), OTError> {
         let mut update_node = self
             .node_id_at_path(path)
             .ok_or_else(|| ErrorBuilder::new(OTErrorCode::PathNotFound).build())?;
@@ -250,7 +263,7 @@ impl NodeTree {
         Ok(())
     }
 
-    fn update_body(&mut self, path: &Path, changeset: &NodeBodyChangeset) -> Result<(), OTError> {
+    fn update_body(&mut self, path: &Path, changeset: NodeBodyChangeset) -> Result<(), OTError> {
         self.mut_node_at_path(path, |node| {
             node.apply_body_changeset(changeset);
             Ok(())
@@ -259,7 +272,7 @@ impl NodeTree {
 
     fn mut_node_at_path<F>(&mut self, path: &Path, f: F) -> Result<(), OTError>
     where
-        F: Fn(&mut Node) -> Result<(), OTError>,
+        F: FnOnce(&mut Node) -> Result<(), OTError>,
     {
         let node_id = self
             .node_id_at_path(path)

+ 9 - 4
shared-lib/lib-ot/src/core/document/operation.rs

@@ -1,4 +1,3 @@
-use crate::core::document::operation_serde::*;
 use crate::core::document::path::Path;
 use crate::core::{NodeAttributes, NodeBodyChangeset, NodeData};
 use crate::errors::OTError;
@@ -18,9 +17,9 @@ pub enum NodeOperation {
         old_attributes: NodeAttributes,
     },
 
-    #[serde(rename = "edit-body")]
-    #[serde(serialize_with = "serialize_edit_body")]
-    // #[serde(deserialize_with = "operation_serde::deserialize_edit_body")]
+    #[serde(rename = "update-body")]
+    // #[serde(serialize_with = "serialize_edit_body")]
+    // #[serde(deserialize_with = "deserialize_edit_body")]
     UpdateBody { path: Path, changeset: NodeBodyChangeset },
 
     #[serde(rename = "delete")]
@@ -106,6 +105,12 @@ pub struct NodeOperationList {
     operations: Vec<NodeOperation>,
 }
 
+impl NodeOperationList {
+    pub fn into_inner(self) -> Vec<NodeOperation> {
+        self.operations
+    }
+}
+
 impl std::ops::Deref for NodeOperationList {
     type Target = Vec<NodeOperation>;
 

+ 105 - 7
shared-lib/lib-ot/src/core/document/operation_serde.rs

@@ -1,7 +1,13 @@
 use crate::core::{NodeBodyChangeset, Path};
+use crate::rich_text::RichTextDelta;
+use serde::de::{self, MapAccess, Visitor};
 use serde::ser::SerializeMap;
-use serde::Serializer;
+use serde::{Deserializer, Serializer};
+use std::convert::TryInto;
+use std::fmt;
+use std::marker::PhantomData;
 
+#[allow(dead_code)]
 pub fn serialize_edit_body<S>(path: &Path, changeset: &NodeBodyChangeset, serializer: S) -> Result<S::Ok, S::Error>
 where
     S: Serializer,
@@ -21,9 +27,101 @@ where
     }
 }
 
-// pub fn deserialize_edit_body<'de, D>(deserializer: D) -> Result<NodeBodyChangeset, D::Error>
-// where
-//     D: Deserializer<'de>,
-// {
-//     todo!()
-// }
+#[allow(dead_code)]
+pub fn deserialize_edit_body<'de, D>(deserializer: D) -> Result<(Path, NodeBodyChangeset), D::Error>
+where
+    D: Deserializer<'de>,
+{
+    struct NodeBodyChangesetVisitor();
+
+    impl<'de> Visitor<'de> for NodeBodyChangesetVisitor {
+        type Value = (Path, NodeBodyChangeset);
+
+        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+            formatter.write_str("Expect Path and NodeBodyChangeset")
+        }
+
+        #[inline]
+        fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
+        where
+            V: MapAccess<'de>,
+        {
+            let mut path: Option<Path> = None;
+            let mut delta_changeset = DeltaBodyChangeset::<V::Error>::new();
+            while let Some(key) = map.next_key()? {
+                match key {
+                    "delta" => {
+                        if delta_changeset.delta.is_some() {
+                            return Err(de::Error::duplicate_field("delta"));
+                        }
+                        delta_changeset.delta = Some(map.next_value()?);
+                    }
+                    "inverted" => {
+                        if delta_changeset.inverted.is_some() {
+                            return Err(de::Error::duplicate_field("inverted"));
+                        }
+                        delta_changeset.inverted = Some(map.next_value()?);
+                    }
+                    "path" => {
+                        if path.is_some() {
+                            return Err(de::Error::duplicate_field("path"));
+                        }
+
+                        path = Some(map.next_value::<Path>()?)
+                    }
+                    other => {
+                        panic!("Unexpected key: {}", other);
+                    }
+                }
+            }
+            if path.is_none() {
+                return Err(de::Error::missing_field("path"));
+            }
+
+            let changeset = delta_changeset.try_into()?;
+
+            Ok((path.unwrap(), changeset))
+        }
+    }
+    deserializer.deserialize_any(NodeBodyChangesetVisitor())
+}
+
+#[allow(dead_code)]
+struct DeltaBodyChangeset<E> {
+    delta: Option<RichTextDelta>,
+    inverted: Option<RichTextDelta>,
+    error: PhantomData<E>,
+}
+
+impl<E> DeltaBodyChangeset<E> {
+    fn new() -> Self {
+        Self {
+            delta: None,
+            inverted: None,
+            error: PhantomData,
+        }
+    }
+}
+
+impl<E> std::convert::TryInto<NodeBodyChangeset> for DeltaBodyChangeset<E>
+where
+    E: de::Error,
+{
+    type Error = E;
+
+    fn try_into(self) -> Result<NodeBodyChangeset, Self::Error> {
+        if self.delta.is_none() {
+            return Err(de::Error::missing_field("delta"));
+        }
+
+        if self.inverted.is_none() {
+            return Err(de::Error::missing_field("inverted"));
+        }
+        let changeset = NodeBodyChangeset::Delta {
+            delta: self.delta.unwrap(),
+            inverted: self.inverted.unwrap(),
+        };
+
+        Ok(changeset)
+    }
+}

+ 23 - 5
shared-lib/lib-ot/src/core/document/transaction.rs

@@ -5,13 +5,31 @@ use indextree::NodeId;
 use super::{NodeBodyChangeset, NodeOperationList};
 
 pub struct Transaction {
-    pub operations: NodeOperationList,
+    operations: NodeOperationList,
 }
 
 impl Transaction {
-    fn new(operations: NodeOperationList) -> Transaction {
+    pub fn new(operations: NodeOperationList) -> Transaction {
         Transaction { operations }
     }
+
+    pub fn into_operations(self) -> Vec<NodeOperation> {
+        self.operations.into_inner()
+    }
+}
+
+impl std::ops::Deref for Transaction {
+    type Target = NodeOperationList;
+
+    fn deref(&self) -> &Self::Target {
+        &self.operations
+    }
+}
+
+impl std::ops::DerefMut for Transaction {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.operations
+    }
 }
 
 pub struct TransactionBuilder<'a> {
@@ -41,13 +59,13 @@ impl<'a> TransactionBuilder<'a> {
     /// //      0 -- text_1
     /// //      1 -- text_2
     /// use lib_ot::core::{NodeTree, NodeData, TransactionBuilder};
-    /// let mut node_tree = NodeTree::new();
+    /// let mut node_tree = NodeTree::new("root");
     /// let transaction = TransactionBuilder::new(&node_tree)
     ///     .insert_nodes_at_path(0,vec![ NodeData::new("text_1"),  NodeData::new("text_2")])
     ///     .finalize();
     ///  node_tree.apply(transaction).unwrap();
     ///
-    ///  node_tree.node_at_path(vec![0, 0]);
+    ///  node_tree.node_id_at_path(vec![0, 0]);
     /// ```
     ///
     pub fn insert_nodes_at_path<T: Into<Path>>(self, path: T, nodes: Vec<NodeData>) -> Self {
@@ -71,7 +89,7 @@ impl<'a> TransactionBuilder<'a> {
     /// // -- 0
     /// //    |-- text
     /// use lib_ot::core::{NodeTree, NodeData, TransactionBuilder};
-    /// let mut node_tree = NodeTree::new();
+    /// let mut node_tree = NodeTree::new("root");
     /// let transaction = TransactionBuilder::new(&node_tree)
     ///     .insert_node_at_path(0, NodeData::new("text"))
     ///     .finalize();

+ 0 - 12
shared-lib/lib-ot/src/rich_text/attributes.rs

@@ -82,17 +82,6 @@ impl TextAttributes {
         self.inner.retain(|k, _| k != &key);
     }
 
-    // pub fn block_attributes_except_header(attributes: &Attributes) -> Attributes
-    // {     let mut new_attributes = Attributes::new();
-    //     attributes.iter().for_each(|(k, v)| {
-    //         if k != &AttributeKey::Header {
-    //             new_attributes.insert(k.clone(), v.clone());
-    //         }
-    //     });
-    //
-    //     new_attributes
-    // }
-
     // Update inner by constructing new attributes from the other if it's
     // not None and replace the key/value with self key/value.
     pub fn merge(&mut self, other: Option<TextAttributes>) {
@@ -298,7 +287,6 @@ pub enum TextAttributeKey {
     Header,
 }
 
-// pub trait AttributeValueData<'a>: Serialize + Deserialize<'a> {}
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct TextAttributeValue(pub Option<String>);
 

+ 15 - 6
shared-lib/lib-ot/tests/node/operation_test.rs

@@ -1,5 +1,6 @@
-use lib_ot::core::{
-    NodeAttributeBuilder, NodeBodyChangeset, NodeBuilder, NodeData, NodeOperation, Path, TextDeltaBuilder,
+use lib_ot::{
+    core::{NodeAttributeBuilder, NodeBodyChangeset, NodeData, NodeDataBuilder, NodeOperation, Path},
+    rich_text::RichTextDeltaBuilder,
 };
 
 #[test]
@@ -14,7 +15,7 @@ fn operation_insert_node_serde_test() {
 
 #[test]
 fn operation_insert_node_with_children_serde_test() {
-    let node = NodeBuilder::new("text")
+    let node = NodeDataBuilder::new("text")
         .add_node(NodeData::new("sub_text".to_owned()))
         .build();
 
@@ -44,8 +45,8 @@ fn operation_update_node_attributes_serde_test() {
 }
 
 #[test]
-fn operation_update_node_body_serde_test() {
-    let delta = TextDeltaBuilder::new().insert("AppFlowy...").build();
+fn operation_update_node_body_serialize_test() {
+    let delta = RichTextDeltaBuilder::new().insert("AppFlowy...").build();
     let inverted = delta.invert_str("");
     let changeset = NodeBodyChangeset::Delta { delta, inverted };
     let insert = NodeOperation::UpdateBody {
@@ -55,7 +56,15 @@ fn operation_update_node_body_serde_test() {
     let result = serde_json::to_string(&insert).unwrap();
     assert_eq!(
         result,
-        r#"{"op":"edit-body","path":[0,1],"delta":[{"insert":"AppFlowy..."}],"inverted":[{"delete":11}]}"#
+        r#"{"op":"update-body","path":[0,1],"changeset":{"delta":{"delta":[{"insert":"AppFlowy..."}],"inverted":[{"delete":11}]}}}"#
     );
     //
 }
+
+#[test]
+fn operation_update_node_body_deserialize_test() {
+    let json_1 = r#"{"op":"update-body","path":[0,1],"changeset":{"delta":{"delta":[{"insert":"AppFlowy..."}],"inverted":[{"delete":11}]}}}"#;
+    let operation: NodeOperation = serde_json::from_str(json_1).unwrap();
+    let json_2 = serde_json::to_string(&operation).unwrap();
+    assert_eq!(json_1, json_2);
+}

+ 7 - 6
shared-lib/lib-ot/tests/node/script.rs

@@ -1,5 +1,6 @@
-use lib_ot::core::{
-    NodeAttributes, NodeBody, NodeBodyChangeset, NodeData, NodeTree, Path, TextDelta, TransactionBuilder,
+use lib_ot::{
+    core::{NodeAttributes, NodeBody, NodeBodyChangeset, NodeData, NodeTree, Path, TransactionBuilder},
+    rich_text::RichTextDelta,
 };
 
 pub enum NodeScript {
@@ -7,9 +8,9 @@ pub enum NodeScript {
     UpdateAttributes { path: Path, attributes: NodeAttributes },
     UpdateBody { path: Path, changeset: NodeBodyChangeset },
     DeleteNode { path: Path },
-    AssertNumberOfChildrenAtPath { path: Option<Path>, len: usize },
+    AssertNumberOfNodesAtPath { path: Option<Path>, len: usize },
     AssertNode { path: Path, expected: Option<NodeData> },
-    AssertNodeDelta { path: Path, expected: TextDelta },
+    AssertNodeDelta { path: Path, expected: RichTextDelta },
 }
 
 pub struct NodeTest {
@@ -19,7 +20,7 @@ pub struct NodeTest {
 impl NodeTest {
     pub fn new() -> Self {
         Self {
-            node_tree: NodeTree::new(),
+            node_tree: NodeTree::new("root"),
         }
     }
 
@@ -68,7 +69,7 @@ impl NodeTest {
                     }
                 }
             }
-            NodeScript::AssertNumberOfChildrenAtPath {
+            NodeScript::AssertNumberOfNodesAtPath {
                 path,
                 len: expected_len,
             } => match path {

+ 60 - 7
shared-lib/lib-ot/tests/node/tree_test.rs

@@ -3,8 +3,8 @@ use crate::node::script::NodeTest;
 use lib_ot::core::NodeBody;
 use lib_ot::core::NodeBodyChangeset;
 use lib_ot::core::OperationTransform;
-use lib_ot::core::TextDeltaBuilder;
-use lib_ot::core::{NodeBuilder, NodeData, Path};
+use lib_ot::core::{NodeData, NodeDataBuilder, Path};
+use lib_ot::rich_text::RichTextDeltaBuilder;
 
 #[test]
 fn node_insert_test() {
@@ -27,7 +27,7 @@ fn node_insert_test() {
 #[test]
 fn node_insert_node_with_children_test() {
     let mut test = NodeTest::new();
-    let inserted_node = NodeBuilder::new("text").add_node(NodeData::new("image")).build();
+    let inserted_node = NodeDataBuilder::new("text").add_node(NodeData::new("image")).build();
     let path: Path = 0.into();
     let scripts = vec![
         InsertNode {
@@ -133,7 +133,7 @@ fn node_insert_node_in_ordered_nodes_test() {
             path: path_4,
             expected: Some(node_3),
         },
-        AssertNumberOfChildrenAtPath { path: None, len: 4 },
+        AssertNumberOfNodesAtPath { path: None, len: 4 },
     ];
     test.run_scripts(scripts);
 }
@@ -186,12 +186,12 @@ fn node_update_body_test() {
     let path: Path = 0.into();
 
     let s = "Hello".to_owned();
-    let init_delta = TextDeltaBuilder::new().insert(&s).build();
-    let delta = TextDeltaBuilder::new().retain(s.len()).insert(" AppFlowy").build();
+    let init_delta = RichTextDeltaBuilder::new().insert(&s).build();
+    let delta = RichTextDeltaBuilder::new().retain(s.len()).insert(" AppFlowy").build();
     let inverted = delta.invert(&init_delta);
     let expected = init_delta.compose(&delta).unwrap();
 
-    let node = NodeBuilder::new("text")
+    let node = NodeDataBuilder::new("text")
         .insert_body(NodeBody::Delta(init_delta))
         .build();
 
@@ -208,3 +208,56 @@ fn node_update_body_test() {
     ];
     test.run_scripts(scripts);
 }
+
+// #[test]
+// fn node_tree_deserial_from_operations_test() {
+//     let mut test = NodeTest::new();
+//     let node: NodeData = serde_json::from_str(EXAMPLE_JSON).unwrap();
+//     let path: Path = 0.into();
+//     test.run_scripts(vec![InsertNode {
+//         path: path.clone(),
+//         node: node.clone(),
+//     }]);
+// }
+
+#[allow(dead_code)]
+const EXAMPLE_JSON: &str = r#"
+{
+  "type": "editor",
+  "children": [
+    {
+      "type": "image",
+      "attributes": {
+        "image_src": "https://s1.ax1x.com/2022/08/26/v2sSbR.jpg",
+        "align": "center"
+      }
+    },
+    {
+      "type": "text",
+      "attributes": {
+        "subtype": "heading",
+        "heading": "h1"
+      },
+      "body": [
+        {
+          "insert": "👋 "
+        },
+        {
+          "insert": "Welcome to ",
+          "attributes": {
+            "bold": true
+          }
+        },
+        {
+          "insert": "AppFlowy Editor",
+          "attributes": {
+            "href": "appflowy.io",
+            "italic": true,
+            "bold": true
+          }
+        }
+      ]
+    }
+  ]
+}
+"#;