Преглед на файлове

Merge pull request #1018 from AppFlowy-IO/refactor/node_delta

refactor: replace delta with node body
Nathan.fooo преди 2 години
родител
ревизия
9142f5d870

+ 5 - 11
shared-lib/lib-ot/src/core/document/attributes.rs

@@ -5,15 +5,9 @@ use std::collections::HashMap;
 
 pub type AttributeMap = HashMap<AttributeKey, AttributeValue>;
 
-#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Debug)]
+#[derive(Default, Clone, Serialize, Deserialize, Eq, PartialEq, Debug)]
 pub struct NodeAttributes(AttributeMap);
 
-impl Default for NodeAttributes {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
 impl std::ops::Deref for NodeAttributes {
     type Target = AttributeMap;
 
@@ -37,14 +31,14 @@ impl NodeAttributes {
         Self(attribute_map)
     }
 
-    pub fn to_inner(&self) -> AttributeMap {
-        self.0.clone()
-    }
-
     pub fn insert<K: ToString, V: Into<AttributeValue>>(&mut self, key: K, value: V) {
         self.0.insert(key.to_string(), value.into());
     }
 
+    pub fn is_empty(&self) -> bool {
+        self.0.is_empty()
+    }
+
     pub fn delete(&mut self, key: &AttributeKey) {
         self.insert(key.clone(), AttributeValue(None));
     }

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

@@ -3,6 +3,7 @@ mod attributes;
 mod node;
 mod node_tree;
 mod operation;
+mod operation_serde;
 mod path;
 mod transaction;
 

+ 150 - 32
shared-lib/lib-ot/src/core/document/node.rs

@@ -1,11 +1,145 @@
-use crate::core::{NodeAttributes, TextDelta};
+use crate::core::NodeBody::Delta;
+use crate::core::{AttributeKey, AttributeValue, NodeAttributes, OperationTransform, TextDelta};
+use crate::errors::OTError;
 use serde::{Deserialize, Serialize};
 
+#[derive(Default, Clone, Serialize, Deserialize, Eq, PartialEq)]
+pub struct Node {
+    #[serde(rename = "type")]
+    pub node_type: String,
+
+    #[serde(skip_serializing_if = "NodeAttributes::is_empty")]
+    pub attributes: NodeAttributes,
+
+    #[serde(skip_serializing_if = "NodeBody::is_empty")]
+    pub body: NodeBody,
+
+    #[serde(skip_serializing_if = "Vec::is_empty")]
+    pub children: Vec<Node>,
+}
+
+impl Node {
+    pub fn new<T: ToString>(node_type: T) -> Node {
+        Node {
+            node_type: node_type.to_string(),
+            ..Default::default()
+        }
+    }
+}
+
+pub struct NodeBuilder {
+    node: Node,
+}
+
+impl NodeBuilder {
+    pub fn new<T: ToString>(node_type: T) -> Self {
+        Self {
+            node: Node::new(node_type.to_string()),
+        }
+    }
+
+    pub fn add_node(mut self, node: Node) -> Self {
+        self.node.children.push(node);
+        self
+    }
+
+    pub fn insert_attribute(mut self, key: AttributeKey, value: AttributeValue) -> Self {
+        self.node.attributes.insert(key, value);
+        self
+    }
+
+    pub fn set_body(mut self, body: NodeBody) -> Self {
+        self.node.body = body;
+        self
+    }
+    pub fn build(self) -> Node {
+        self.node
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub enum NodeBody {
+    Empty,
+    Delta(TextDelta),
+}
+
+impl std::default::Default for NodeBody {
+    fn default() -> Self {
+        NodeBody::Empty
+    }
+}
+
+impl NodeBody {
+    fn is_empty(&self) -> bool {
+        match self {
+            NodeBody::Empty => true,
+            _ => false,
+        }
+    }
+}
+
+impl OperationTransform for NodeBody {
+    fn compose(&self, other: &Self) -> Result<Self, OTError>
+    where
+        Self: Sized,
+    {
+        match (self, other) {
+            (Delta(a), Delta(b)) => a.compose(b).map(|delta| Delta(delta)),
+            (NodeBody::Empty, NodeBody::Empty) => Ok(NodeBody::Empty),
+            (l, r) => {
+                let msg = format!("{:?} can not compose {:?}", l, r);
+                Err(OTError::internal().context(msg))
+            }
+        }
+    }
+
+    fn transform(&self, other: &Self) -> Result<(Self, Self), OTError>
+    where
+        Self: Sized,
+    {
+        match (self, other) {
+            (Delta(l), Delta(r)) => l.transform(r).map(|(ta, tb)| (Delta(ta), Delta(tb))),
+            (NodeBody::Empty, NodeBody::Empty) => Ok((NodeBody::Empty, NodeBody::Empty)),
+            (l, r) => {
+                let msg = format!("{:?} can not compose {:?}", l, r);
+                Err(OTError::internal().context(msg))
+            }
+        }
+    }
+
+    fn invert(&self, other: &Self) -> Self {
+        match (self, other) {
+            (Delta(l), Delta(r)) => Delta(l.invert(r)),
+            (NodeBody::Empty, NodeBody::Empty) => NodeBody::Empty,
+            (l, r) => {
+                tracing::error!("{:?} can not compose {:?}", l, r);
+                l.clone()
+            }
+        }
+    }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum NodeBodyChangeset {
+    Delta { delta: TextDelta, inverted: TextDelta },
+}
+
+impl NodeBodyChangeset {
+    pub fn inverted(&self) -> NodeBodyChangeset {
+        match self {
+            NodeBodyChangeset::Delta { delta, inverted } => NodeBodyChangeset::Delta {
+                delta: inverted.clone(),
+                inverted: delta.clone(),
+            },
+        }
+    }
+}
+
 #[derive(Clone, Eq, PartialEq, Debug)]
 pub struct NodeData {
     pub node_type: String,
+    pub body: NodeBody,
     pub attributes: NodeAttributes,
-    pub delta: Option<TextDelta>,
 }
 
 impl NodeData {
@@ -13,7 +147,16 @@ impl NodeData {
         NodeData {
             node_type: node_type.into(),
             attributes: NodeAttributes::new(),
-            delta: None,
+            body: NodeBody::Empty,
+        }
+    }
+
+    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,
+                Err(e) => tracing::error!("{:?}", e),
+            },
         }
     }
 }
@@ -21,9 +164,9 @@ impl NodeData {
 impl std::convert::From<Node> for NodeData {
     fn from(node: Node) -> Self {
         Self {
-            node_type: node.note_type,
+            node_type: node.node_type,
             attributes: node.attributes,
-            delta: node.delta,
+            body: node.body,
         }
     }
 }
@@ -31,34 +174,9 @@ impl std::convert::From<Node> for NodeData {
 impl std::convert::From<&Node> for NodeData {
     fn from(node: &Node) -> Self {
         Self {
-            node_type: node.note_type.clone(),
+            node_type: node.node_type.clone(),
             attributes: node.attributes.clone(),
-            delta: node.delta.clone(),
-        }
-    }
-}
-
-#[derive(Clone, Serialize, Deserialize, Eq, PartialEq)]
-pub struct Node {
-    #[serde(rename = "type")]
-    pub note_type: String,
-
-    pub attributes: NodeAttributes,
-
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub delta: Option<TextDelta>,
-
-    #[serde(skip_serializing_if = "Vec::is_empty")]
-    pub children: Vec<Node>,
-}
-
-impl Node {
-    pub fn new(node_type: &str) -> Node {
-        Node {
-            note_type: node_type.into(),
-            attributes: NodeAttributes::new(),
-            delta: None,
-            children: Vec::new(),
+            body: node.body.clone(),
         }
     }
 }

+ 9 - 16
shared-lib/lib-ot/src/core/document/node_tree.rs

@@ -1,5 +1,5 @@
 use crate::core::document::path::Path;
-use crate::core::{Node, NodeAttributes, NodeData, NodeOperation, OperationTransform, TextDelta, Transaction};
+use crate::core::{Node, NodeAttributes, NodeBodyChangeset, NodeData, NodeOperation, OperationTransform, Transaction};
 use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
 use indextree::{Arena, Children, FollowingSiblings, NodeId};
 
@@ -30,7 +30,7 @@ impl NodeTree {
     ///
     /// ```
     /// use lib_ot::core::{NodeOperation, NodeTree, Node, Path};
-    /// let nodes = vec![Node::new("text")];
+    /// let nodes = vec![Node::new("text".to_string())];
     /// let root_path: Path = vec![0].into();
     /// let op = NodeOperation::Insert {path: root_path.clone(),nodes };
     ///
@@ -92,7 +92,7 @@ impl NodeTree {
     ///
     /// ```
     /// use lib_ot::core::{NodeOperation, NodeTree, Node, Path};
-    /// let node = Node::new("text");
+    /// let node = Node::new("text".to_string());
     /// let inserted_path: Path = vec![0].into();
     ///
     /// let mut node_tree = NodeTree::new();
@@ -100,7 +100,7 @@ impl NodeTree {
     ///
     /// let inserted_note = node_tree.node_at_path(&inserted_path).unwrap();
     /// let inserted_data = node_tree.get_node_data(inserted_note).unwrap();
-    /// assert_eq!(inserted_data.node_type, node.note_type);
+    /// assert_eq!(inserted_data.node_type, node.node_type);
     /// ```
     pub fn child_from_node_at_index(&self, node_id: NodeId, index: usize) -> Option<NodeId> {
         let children = node_id.children(&self.arena);
@@ -147,7 +147,7 @@ impl NodeTree {
             NodeOperation::Insert { path, nodes } => self.insert_nodes(path, nodes),
             NodeOperation::Update { path, attributes, .. } => self.update_node(path, attributes),
             NodeOperation::Delete { path, nodes } => self.delete_node(path, nodes),
-            NodeOperation::TextEdit { path, delta, .. } => self.update_delta(path, delta),
+            NodeOperation::EditBody { path, changeset: body } => self.update_body(path, body),
         }
     }
 
@@ -216,6 +216,7 @@ impl NodeTree {
         let mut update_node = self
             .node_at_path(path)
             .ok_or_else(|| ErrorBuilder::new(OTErrorCode::PathNotFound).build())?;
+
         for _ in 0..nodes.len() {
             let next = update_node.following_siblings(&self.arena).next();
             update_node.remove_subtree(&mut self.arena);
@@ -228,19 +229,11 @@ impl NodeTree {
         Ok(())
     }
 
-    fn update_delta(&mut self, path: &Path, delta: &TextDelta) -> Result<(), OTError> {
+    fn update_body(&mut self, path: &Path, changeset: &NodeBodyChangeset) -> Result<(), OTError> {
         self.mut_node_at_path(path, |node_data| {
-            if node_data.delta.is_none() {
-                let msg = format!("The delta of the node at path:{:?} should not be empty", path);
-                tracing::error!("{}", msg);
-                return Err(OTError::new(OTErrorCode::UnexpectedEmpty, msg));
-            }
-            let new_delta = node_data.delta.as_ref().unwrap().compose(delta)?;
-            let _ = node_data.delta = Some(new_delta);
+            node_data.apply_body_changeset(changeset);
             Ok(())
-        })?;
-
-        Ok(())
+        })
     }
 
     fn mut_node_at_path<F>(&mut self, path: &Path, f: F) -> Result<(), OTError>

+ 27 - 32
shared-lib/lib-ot/src/core/document/operation.rs

@@ -1,11 +1,13 @@
+use crate::core::document::operation_serde::*;
 use crate::core::document::path::Path;
-use crate::core::{Node, NodeAttributes, TextDelta};
+use crate::core::{Node, NodeAttributes, NodeBodyChangeset};
 
 #[derive(Clone, serde::Serialize, serde::Deserialize)]
 #[serde(tag = "op")]
 pub enum NodeOperation {
     #[serde(rename = "insert")]
     Insert { path: Path, nodes: Vec<Node> },
+
     #[serde(rename = "update")]
     Update {
         path: Path,
@@ -13,14 +15,14 @@ pub enum NodeOperation {
         #[serde(rename = "oldAttributes")]
         old_attributes: NodeAttributes,
     },
+
     #[serde(rename = "delete")]
     Delete { path: Path, nodes: Vec<Node> },
-    #[serde(rename = "text-edit")]
-    TextEdit {
-        path: Path,
-        delta: TextDelta,
-        inverted: TextDelta,
-    },
+
+    #[serde(rename = "edit-body")]
+    #[serde(serialize_with = "serialize_edit_body")]
+    // #[serde(deserialize_with = "operation_serde::deserialize_edit_body")]
+    EditBody { path: Path, changeset: NodeBodyChangeset },
 }
 
 impl NodeOperation {
@@ -29,7 +31,7 @@ impl NodeOperation {
             NodeOperation::Insert { path, .. } => path,
             NodeOperation::Update { path, .. } => path,
             NodeOperation::Delete { path, .. } => path,
-            NodeOperation::TextEdit { path, .. } => path,
+            NodeOperation::EditBody { path, .. } => path,
         }
     }
     pub fn invert(&self) -> NodeOperation {
@@ -51,10 +53,9 @@ impl NodeOperation {
                 path: path.clone(),
                 nodes: nodes.clone(),
             },
-            NodeOperation::TextEdit { path, delta, inverted } => NodeOperation::TextEdit {
+            NodeOperation::EditBody { path, changeset: body } => NodeOperation::EditBody {
                 path: path.clone(),
-                delta: inverted.clone(),
-                inverted: delta.clone(),
+                changeset: body.inverted(),
             },
         }
     }
@@ -77,10 +78,9 @@ impl NodeOperation {
                 path,
                 nodes: nodes.clone(),
             },
-            NodeOperation::TextEdit { delta, inverted, .. } => NodeOperation::TextEdit {
-                path,
-                delta: delta.clone(),
-                inverted: inverted.clone(),
+            NodeOperation::EditBody { path, changeset: body } => NodeOperation::EditBody {
+                path: path.clone(),
+                changeset: body.clone(),
             },
         }
     }
@@ -101,35 +101,27 @@ impl NodeOperation {
 
 #[cfg(test)]
 mod tests {
-    use crate::core::{Delta, Node, NodeAttributes, NodeOperation, Path};
+    use crate::core::{Node, NodeAttributes, NodeBodyChangeset, NodeBuilder, NodeOperation, Path, TextDelta};
     #[test]
     fn test_serialize_insert_operation() {
         let insert = NodeOperation::Insert {
             path: Path(vec![0, 1]),
-            nodes: vec![Node::new("text")],
+            nodes: vec![Node::new("text".to_owned())],
         };
         let result = serde_json::to_string(&insert).unwrap();
-        assert_eq!(
-            result,
-            r#"{"op":"insert","path":[0,1],"nodes":[{"type":"text","attributes":{}}]}"#
-        );
+        assert_eq!(result, r#"{"op":"insert","path":[0,1],"nodes":[{"type":"text"}]}"#);
     }
 
     #[test]
     fn test_serialize_insert_sub_trees() {
         let insert = NodeOperation::Insert {
             path: Path(vec![0, 1]),
-            nodes: vec![Node {
-                note_type: "text".into(),
-                attributes: NodeAttributes::new(),
-                delta: None,
-                children: vec![Node::new("text")],
-            }],
+            nodes: vec![NodeBuilder::new("text").add_node(Node::new("text".to_owned())).build()],
         };
         let result = serde_json::to_string(&insert).unwrap();
         assert_eq!(
             result,
-            r#"{"op":"insert","path":[0,1],"nodes":[{"type":"text","attributes":{},"children":[{"type":"text","attributes":{}}]}]}"#
+            r#"{"op":"insert","path":[0,1],"nodes":[{"type":"text","children":[{"type":"text"}]}]}"#
         );
     }
 
@@ -149,12 +141,15 @@ mod tests {
 
     #[test]
     fn test_serialize_text_edit_operation() {
-        let insert = NodeOperation::TextEdit {
+        let changeset = NodeBodyChangeset::Delta {
+            delta: TextDelta::new(),
+            inverted: TextDelta::new(),
+        };
+        let insert = NodeOperation::EditBody {
             path: Path(vec![0, 1]),
-            delta: Delta::new(),
-            inverted: Delta::new(),
+            changeset,
         };
         let result = serde_json::to_string(&insert).unwrap();
-        assert_eq!(result, r#"{"op":"text-edit","path":[0,1],"delta":[],"inverted":[]}"#);
+        assert_eq!(result, r#"{"op":"edit-body","path":[0,1],"delta":[],"inverted":[]}"#);
     }
 }

+ 29 - 0
shared-lib/lib-ot/src/core/document/operation_serde.rs

@@ -0,0 +1,29 @@
+use crate::core::{NodeBodyChangeset, Path};
+use serde::ser::SerializeMap;
+use serde::Serializer;
+
+pub fn serialize_edit_body<S>(path: &Path, changeset: &NodeBodyChangeset, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    let mut map = serializer.serialize_map(Some(3))?;
+    map.serialize_key("path")?;
+    map.serialize_value(path)?;
+
+    match changeset {
+        NodeBodyChangeset::Delta { delta, inverted } => {
+            map.serialize_key("delta")?;
+            map.serialize_value(delta)?;
+            map.serialize_key("inverted")?;
+            map.serialize_value(inverted)?;
+            map.end()
+        }
+    }
+}
+
+// pub fn deserialize_edit_body<'de, D>(deserializer: D) -> Result<NodeBodyChangeset, D::Error>
+// where
+//     D: Deserializer<'de>,
+// {
+//     todo!()
+// }

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

@@ -127,9 +127,9 @@ impl<'a> TransactionBuilder<'a> {
         });
 
         Node {
-            note_type: node_data.node_type.clone(),
+            node_type: node_data.node_type.clone(),
             attributes: node_data.attributes.clone(),
-            delta: node_data.delta.clone(),
+            body: node_data.body.clone(),
             children,
         }
     }

+ 2 - 7
shared-lib/lib-ot/tests/node/test.rs

@@ -1,6 +1,6 @@
 use crate::node::script::NodeScript::*;
 use crate::node::script::NodeTest;
-use lib_ot::core::{Node, NodeAttributes, Path};
+use lib_ot::core::{Node, NodeBuilder, Path};
 
 #[test]
 fn node_insert_test() {
@@ -23,12 +23,7 @@ fn node_insert_test() {
 #[test]
 fn node_insert_node_with_children_test() {
     let mut test = NodeTest::new();
-    let inserted_node = Node {
-        note_type: "text".into(),
-        attributes: NodeAttributes::new(),
-        delta: None,
-        children: vec![Node::new("image")],
-    };
+    let inserted_node = NodeBuilder::new("text").add_node(Node::new("image")).build();
     let path: Path = 0.into();
     let scripts = vec![
         InsertNode {