Bladeren bron

chore: add document serde

nathan 2 jaren geleden
bovenliggende
commit
88942ab826

+ 38 - 33
frontend/rust-lib/flowy-document/src/editor/document.rs

@@ -4,44 +4,49 @@ use flowy_revision::{RevisionObjectDeserializer, RevisionObjectSerializer};
 use flowy_sync::entities::document::DocumentPayloadPB;
 use flowy_sync::entities::revision::Revision;
 use flowy_sync::util::make_operations_from_revisions;
-use lib_ot::core::{AttributeHashMap, Transaction};
+use lib_ot::core::{AttributeHashMap, NodeTree, Transaction};
+use serde::{Deserialize, Serialize};
 
+#[derive(Serialize, Deserialize, Debug)]
 pub struct Document {
-    doc_id: String,
+    #[serde(serialize_with = "serialize_tree")]
+    #[serde(deserialize_with = "deserialize_tree")]
+    tree: NodeTree,
 }
 
 impl Document {
-    pub fn from_content(content: &str) -> FlowyResult<Self> {
-        todo!()
+    pub fn from_transaction(transaction: Transaction) -> FlowyResult<Self> {
+        let tree = NodeTree::from_operations("root", transaction.operations)?;
+        Ok(Self { tree })
     }
 }
 
-pub struct DocumentRevisionSerde();
-impl RevisionObjectDeserializer for DocumentRevisionSerde {
-    type Output = DocumentPayloadPB;
-
-    fn deserialize_revisions(object_id: &str, revisions: Vec<Revision>) -> FlowyResult<Self::Output> {
-        let (base_rev_id, rev_id) = revisions.last().unwrap().pair_rev_id();
-
-        for revision in revisions {
-            let transaction = Transaction::from_bytes(&revision.bytes)?;
-        }
-
-        let mut delta = make_operations_from_revisions(revisions)?;
-        correct_delta(&mut delta);
-
-        Result::<DocumentPayloadPB, FlowyError>::Ok(DocumentPayloadPB {
-            doc_id: object_id.to_owned(),
-            content: delta.json_str(),
-            rev_id,
-            base_rev_id,
-        })
-    }
-}
-
-impl RevisionObjectSerializer for DocumentRevisionSerde {
-    fn serialize_revisions(revisions: Vec<Revision>) -> FlowyResult<Bytes> {
-        let operations = make_operations_from_revisions::<AttributeHashMap>(revisions)?;
-        Ok(operations.json_bytes())
-    }
-}
+// pub struct DocumentRevisionSerde();
+// impl RevisionObjectDeserializer for DocumentRevisionSerde {
+//     type Output = DocumentPayloadPB;
+//
+//     fn deserialize_revisions(object_id: &str, revisions: Vec<Revision>) -> FlowyResult<Self::Output> {
+//         let (base_rev_id, rev_id) = revisions.last().unwrap().pair_rev_id();
+//
+//         for revision in revisions {
+//             let transaction = Transaction::from_bytes(&revision.bytes)?;
+//         }
+//
+//         let mut delta = make_operations_from_revisions(revisions)?;
+//         correct_delta(&mut delta);
+//
+//         Result::<DocumentPayloadPB, FlowyError>::Ok(DocumentPayloadPB {
+//             doc_id: object_id.to_owned(),
+//             content: delta.json_str(),
+//             rev_id,
+//             base_rev_id,
+//         })
+//     }
+// }
+//
+// impl RevisionObjectSerializer for DocumentRevisionSerde {
+//     fn serialize_revisions(revisions: Vec<Revision>) -> FlowyResult<Bytes> {
+//         let operations = make_operations_from_revisions::<AttributeHashMap>(revisions)?;
+//         Ok(operations.json_bytes())
+//     }
+// }

+ 14 - 0
frontend/rust-lib/flowy-document/src/editor/document_serde.rs

@@ -0,0 +1,14 @@
+pub fn serialize_document_tree<S>(body: &Body, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    let mut map = serializer.serialize_map(Some(3))?;
+    match body {
+        Body::Empty => {}
+        Body::Delta(delta) => {
+            map.serialize_key("delta")?;
+            map.serialize_value(delta)?;
+        }
+    }
+    map.end()
+}

+ 1 - 0
frontend/rust-lib/flowy-document/src/editor/mod.rs

@@ -1,4 +1,5 @@
 mod document;
+mod document_serde;
 mod editor;
 
 pub use editor::*;

+ 0 - 2
frontend/rust-lib/flowy-document/tests/document/mod.rs

@@ -1,2 +0,0 @@
-mod script;
-mod text_block_test;

+ 1 - 1
frontend/rust-lib/flowy-document/tests/main.rs

@@ -1,2 +1,2 @@
-mod document;
 mod editor;
+mod old_document;

+ 2 - 0
frontend/rust-lib/flowy-document/tests/old_document/mod.rs

@@ -0,0 +1,2 @@
+mod old_document_test;
+mod script;

+ 1 - 1
frontend/rust-lib/flowy-document/tests/document/text_block_test.rs → frontend/rust-lib/flowy-document/tests/old_document/old_document_test.rs

@@ -1,4 +1,4 @@
-use crate::document::script::{EditorScript::*, *};
+use crate::old_document::script::{EditorScript::*, *};
 use flowy_revision::disk::RevisionState;
 use lib_ot::core::{count_utf16_code_units, Interval};
 

+ 0 - 0
frontend/rust-lib/flowy-document/tests/document/script.rs → frontend/rust-lib/flowy-document/tests/old_document/script.rs


+ 4 - 0
shared-lib/lib-ot/src/core/attributes/attribute.rs

@@ -48,6 +48,10 @@ impl AttributeHashMap {
         AttributeHashMap(HashMap::new())
     }
 
+    pub fn into_inner(self) -> HashMap<AttributeKey, AttributeValue> {
+        self.0
+    }
+
     pub fn from_value(attribute_map: HashMap<AttributeKey, AttributeValue>) -> Self {
         Self(attribute_map)
     }

+ 1 - 1
shared-lib/lib-ot/src/core/node_tree/node.rs

@@ -58,7 +58,7 @@ impl NodeDataBuilder {
     }
 
     /// Appends a new node to the end of the builder's node children.
-    pub fn add_node(mut self, node: NodeData) -> Self {
+    pub fn add_node_data(mut self, node: NodeData) -> Self {
         self.node.children.push(node);
         self
     }

+ 40 - 4
shared-lib/lib-ot/src/core/node_tree/tree.rs

@@ -1,11 +1,10 @@
-use crate::core::{Changeset, Node, NodeData, NodeOperation, Path, Transaction};
+use super::NodeOperations;
+use crate::core::{Changeset, Node, NodeData, NodeDataBuilder, NodeOperation, Path, Transaction};
 use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
 use indextree::{Arena, Children, FollowingSiblings, NodeId};
 use std::rc::Rc;
 
-use super::NodeOperations;
-
-///
+#[derive(Debug)]
 pub struct NodeTree {
     arena: Arena<Node>,
     root: NodeId,
@@ -48,6 +47,43 @@ impl NodeTree {
         }
     }
 
+    pub fn get_node_data(&self, node_id: NodeId) -> Option<NodeData> {
+        let Node {
+            node_type,
+            body,
+            attributes,
+        } = self.get_node(node_id)?.clone();
+        let mut node_data = NodeData::new(node_type);
+        for (key, value) in attributes.into_inner() {
+            node_data.attributes.insert(key, value);
+        }
+        node_data.body = body;
+
+        let children = self.children_from_node(node_id);
+        for child in children.into_iter() {
+            if let Some(child_node_data) = self.get_node_data(child) {
+                node_data.children.push(child_node_data);
+            }
+        }
+        Some(node_data)
+    }
+
+    pub fn root_node(&self) -> NodeId {
+        self.root
+    }
+
+    pub fn to_json(&self, node_id: NodeId, pretty_json: bool) -> Result<String, OTError> {
+        let node_data = self
+            .get_node_data(node_id)
+            .ok_or(OTError::internal().context("Node doesn't exist exist"))?;
+        let json = if pretty_json {
+            serde_json::to_string_pretty(&node_data).map_err(|err| OTError::serde().context(err))?
+        } else {
+            serde_json::to_string(&node_data).map_err(|err| OTError::serde().context(err))?
+        };
+        Ok(json)
+    }
+
     ///
     /// # Examples
     ///

+ 6 - 29
shared-lib/lib-ot/tests/node/editor_test.rs

@@ -32,11 +32,7 @@ fn editor_deserialize_node_test() {
         AssertNumberOfNodesAtPath { path: None, len: 1 },
         AssertNumberOfNodesAtPath {
             path: Some(0.into()),
-            len: 14,
-        },
-        AssertNumberOfNodesAtPath {
-            path: Some(0.into()),
-            len: 4,
+            len: 2,
         },
         AssertNodeDelta {
             path: vec![0, 1].into(),
@@ -46,29 +42,25 @@ fn editor_deserialize_node_test() {
             path: vec![0, 0].into(),
             expected: Some(node.children[0].clone()),
         },
-        AssertNodeData {
-            path: vec![0, 3].into(),
-            expected: Some(node.children[3].clone()),
+        AssertNodeJSON {
+            expected: EXAMPLE_JSON.to_string(),
         },
     ]);
 }
 
 #[allow(dead_code)]
-const EXAMPLE_JSON: &str = r#"
-{
+const EXAMPLE_JSON: &str = r#"{
   "type": "editor",
   "children": [
     {
       "type": "image",
       "attributes": {
-        "image_src": "https://s1.ax1x.com/2022/08/26/v2sSbR.jpg",
-        "align": "center"
+        "image_src": "https://s1.ax1x.com/2022/08/26/v2sSbR.jpg"
       }
     },
     {
       "type": "text",
       "attributes": {
-        "subtype": "heading",
         "heading": "h1"
       },
       "body": {
@@ -90,21 +82,6 @@ const EXAMPLE_JSON: &str = r#"
           }
         ]
       }
-    },
-    { "type": "text", "delta": [] },
-    {
-      "type": "text",
-      "body": {
-        "delta": [
-            { "insert": "AppFlowy Editor is a " },
-            { "insert": "highly customizable", "attributes": { "bold": true } },
-            { "insert": " " },
-            { "insert": "rich-text editor", "attributes": { "italic": true } },
-            { "insert": " for " },
-            { "insert": "Flutter", "attributes": { "underline": true } }
-        ]
-      }
     }
   ]
-}
-"#;
+}"#;

+ 4 - 4
shared-lib/lib-ot/tests/node/operation_test.rs

@@ -75,13 +75,13 @@ fn operation_insert_transform_one_level_path_test() {
 fn operation_insert_transform_multiple_level_path_test() {
     let mut test = NodeTest::new();
     let node_data_1 = NodeDataBuilder::new("text_1")
-        .add_node(NodeDataBuilder::new("text_1_1").build())
-        .add_node(NodeDataBuilder::new("text_1_2").build())
+        .add_node_data(NodeDataBuilder::new("text_1_1").build())
+        .add_node_data(NodeDataBuilder::new("text_1_2").build())
         .build();
 
     let node_data_2 = NodeDataBuilder::new("text_2")
-        .add_node(NodeDataBuilder::new("text_2_1").build())
-        .add_node(NodeDataBuilder::new("text_2_2").build())
+        .add_node_data(NodeDataBuilder::new("text_2_1").build())
+        .add_node_data(NodeDataBuilder::new("text_2_2").build())
         .build();
 
     let node_data_3 = NodeDataBuilder::new("text_3").build();

+ 9 - 0
shared-lib/lib-ot/tests/node/script.rs

@@ -40,6 +40,9 @@ pub enum NodeScript {
         path: Path,
         expected: TextOperations,
     },
+    AssertNodeJSON {
+        expected: String,
+    },
 }
 
 pub struct NodeTest {
@@ -138,6 +141,12 @@ impl NodeTest {
                     panic!("Node body type not match, expect Delta");
                 }
             }
+            NodeScript::AssertNodeJSON { expected } => {
+                let mut children = self.node_tree.children_from_node(self.node_tree.root_node());
+                let node = children.next().unwrap();
+                let json = self.node_tree.to_json(node, true).unwrap();
+                assert_eq!(json, expected)
+            }
         }
     }
 

+ 1 - 1
shared-lib/lib-ot/tests/node/serde_test.rs

@@ -16,7 +16,7 @@ fn operation_insert_node_serde_test() {
 #[test]
 fn operation_insert_node_with_children_serde_test() {
     let node = NodeDataBuilder::new("text")
-        .add_node(NodeData::new("sub_text".to_owned()))
+        .add_node_data(NodeData::new("sub_text".to_owned()))
         .build();
 
     let insert = NodeOperation::Insert {

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

@@ -28,7 +28,9 @@ fn node_insert_test() {
 #[test]
 fn node_insert_node_with_children_test() {
     let mut test = NodeTest::new();
-    let inserted_node = NodeDataBuilder::new("text").add_node(NodeData::new("image")).build();
+    let inserted_node = NodeDataBuilder::new("text")
+        .add_node_data(NodeData::new("image"))
+        .build();
     let path: Path = 0.into();
     let scripts = vec![
         InsertNode {
@@ -152,15 +154,15 @@ fn node_insert_nested_nodes_test() {
     let node_data_1_1 = NodeDataBuilder::new("text_1_1").build();
     let node_data_1_2 = NodeDataBuilder::new("text_1_2").build();
     let node_data_1 = NodeDataBuilder::new("text_1")
-        .add_node(node_data_1_1.clone())
-        .add_node(node_data_1_2.clone())
+        .add_node_data(node_data_1_1.clone())
+        .add_node_data(node_data_1_2.clone())
         .build();
 
     let node_data_2_1 = NodeDataBuilder::new("text_2_1").build();
     let node_data_2_2 = NodeDataBuilder::new("text_2_2").build();
     let node_data_2 = NodeDataBuilder::new("text_2")
-        .add_node(node_data_2_1.clone())
-        .add_node(node_data_2_2.clone())
+        .add_node_data(node_data_2_1.clone())
+        .add_node_data(node_data_2_2.clone())
         .build();
 
     let scripts = vec![
@@ -207,8 +209,8 @@ fn node_insert_node_before_existing_nested_nodes_test() {
     let node_data_1_1 = NodeDataBuilder::new("text_1_1").build();
     let node_data_1_2 = NodeDataBuilder::new("text_1_2").build();
     let node_data_1 = NodeDataBuilder::new("text_1")
-        .add_node(node_data_1_1.clone())
-        .add_node(node_data_1_2.clone())
+        .add_node_data(node_data_1_1.clone())
+        .add_node_data(node_data_1_2.clone())
         .build();
 
     let scripts = vec![