mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #1018 from AppFlowy-IO/refactor/node_delta
refactor: replace delta with node body
This commit is contained in:
commit
9142f5d870
@ -5,15 +5,9 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
pub type AttributeMap = HashMap<AttributeKey, AttributeValue>;
|
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);
|
pub struct NodeAttributes(AttributeMap);
|
||||||
|
|
||||||
impl Default for NodeAttributes {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Deref for NodeAttributes {
|
impl std::ops::Deref for NodeAttributes {
|
||||||
type Target = AttributeMap;
|
type Target = AttributeMap;
|
||||||
|
|
||||||
@ -37,14 +31,14 @@ impl NodeAttributes {
|
|||||||
Self(attribute_map)
|
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) {
|
pub fn insert<K: ToString, V: Into<AttributeValue>>(&mut self, key: K, value: V) {
|
||||||
self.0.insert(key.to_string(), value.into());
|
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) {
|
pub fn delete(&mut self, key: &AttributeKey) {
|
||||||
self.insert(key.clone(), AttributeValue(None));
|
self.insert(key.clone(), AttributeValue(None));
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ mod attributes;
|
|||||||
mod node;
|
mod node;
|
||||||
mod node_tree;
|
mod node_tree;
|
||||||
mod operation;
|
mod operation;
|
||||||
|
mod operation_serde;
|
||||||
mod path;
|
mod path;
|
||||||
mod transaction;
|
mod transaction;
|
||||||
|
|
||||||
|
@ -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};
|
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)]
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
pub struct NodeData {
|
pub struct NodeData {
|
||||||
pub node_type: String,
|
pub node_type: String,
|
||||||
|
pub body: NodeBody,
|
||||||
pub attributes: NodeAttributes,
|
pub attributes: NodeAttributes,
|
||||||
pub delta: Option<TextDelta>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeData {
|
impl NodeData {
|
||||||
@ -13,7 +147,16 @@ impl NodeData {
|
|||||||
NodeData {
|
NodeData {
|
||||||
node_type: node_type.into(),
|
node_type: node_type.into(),
|
||||||
attributes: NodeAttributes::new(),
|
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 {
|
impl std::convert::From<Node> for NodeData {
|
||||||
fn from(node: Node) -> Self {
|
fn from(node: Node) -> Self {
|
||||||
Self {
|
Self {
|
||||||
node_type: node.note_type,
|
node_type: node.node_type,
|
||||||
attributes: node.attributes,
|
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 {
|
impl std::convert::From<&Node> for NodeData {
|
||||||
fn from(node: &Node) -> Self {
|
fn from(node: &Node) -> Self {
|
||||||
Self {
|
Self {
|
||||||
node_type: node.note_type.clone(),
|
node_type: node.node_type.clone(),
|
||||||
attributes: node.attributes.clone(),
|
attributes: node.attributes.clone(),
|
||||||
delta: node.delta.clone(),
|
body: node.body.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(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::core::document::path::Path;
|
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 crate::errors::{ErrorBuilder, OTError, OTErrorCode};
|
||||||
use indextree::{Arena, Children, FollowingSiblings, NodeId};
|
use indextree::{Arena, Children, FollowingSiblings, NodeId};
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ impl NodeTree {
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use lib_ot::core::{NodeOperation, NodeTree, Node, Path};
|
/// 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 root_path: Path = vec![0].into();
|
||||||
/// let op = NodeOperation::Insert {path: root_path.clone(),nodes };
|
/// let op = NodeOperation::Insert {path: root_path.clone(),nodes };
|
||||||
///
|
///
|
||||||
@ -92,7 +92,7 @@ impl NodeTree {
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use lib_ot::core::{NodeOperation, NodeTree, Node, Path};
|
/// 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 inserted_path: Path = vec![0].into();
|
||||||
///
|
///
|
||||||
/// let mut node_tree = NodeTree::new();
|
/// 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_note = node_tree.node_at_path(&inserted_path).unwrap();
|
||||||
/// let inserted_data = node_tree.get_node_data(inserted_note).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> {
|
pub fn child_from_node_at_index(&self, node_id: NodeId, index: usize) -> Option<NodeId> {
|
||||||
let children = node_id.children(&self.arena);
|
let children = node_id.children(&self.arena);
|
||||||
@ -147,7 +147,7 @@ impl NodeTree {
|
|||||||
NodeOperation::Insert { path, nodes } => self.insert_nodes(path, nodes),
|
NodeOperation::Insert { path, nodes } => self.insert_nodes(path, nodes),
|
||||||
NodeOperation::Update { path, attributes, .. } => self.update_node(path, attributes),
|
NodeOperation::Update { path, attributes, .. } => self.update_node(path, attributes),
|
||||||
NodeOperation::Delete { path, nodes } => self.delete_node(path, nodes),
|
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
|
let mut update_node = self
|
||||||
.node_at_path(path)
|
.node_at_path(path)
|
||||||
.ok_or_else(|| ErrorBuilder::new(OTErrorCode::PathNotFound).build())?;
|
.ok_or_else(|| ErrorBuilder::new(OTErrorCode::PathNotFound).build())?;
|
||||||
|
|
||||||
for _ in 0..nodes.len() {
|
for _ in 0..nodes.len() {
|
||||||
let next = update_node.following_siblings(&self.arena).next();
|
let next = update_node.following_siblings(&self.arena).next();
|
||||||
update_node.remove_subtree(&mut self.arena);
|
update_node.remove_subtree(&mut self.arena);
|
||||||
@ -228,19 +229,11 @@ impl NodeTree {
|
|||||||
Ok(())
|
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| {
|
self.mut_node_at_path(path, |node_data| {
|
||||||
if node_data.delta.is_none() {
|
node_data.apply_body_changeset(changeset);
|
||||||
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);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mut_node_at_path<F>(&mut self, path: &Path, f: F) -> Result<(), OTError>
|
fn mut_node_at_path<F>(&mut self, path: &Path, f: F) -> Result<(), OTError>
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
|
use crate::core::document::operation_serde::*;
|
||||||
use crate::core::document::path::Path;
|
use crate::core::document::path::Path;
|
||||||
use crate::core::{Node, NodeAttributes, TextDelta};
|
use crate::core::{Node, NodeAttributes, NodeBodyChangeset};
|
||||||
|
|
||||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(tag = "op")]
|
#[serde(tag = "op")]
|
||||||
pub enum NodeOperation {
|
pub enum NodeOperation {
|
||||||
#[serde(rename = "insert")]
|
#[serde(rename = "insert")]
|
||||||
Insert { path: Path, nodes: Vec<Node> },
|
Insert { path: Path, nodes: Vec<Node> },
|
||||||
|
|
||||||
#[serde(rename = "update")]
|
#[serde(rename = "update")]
|
||||||
Update {
|
Update {
|
||||||
path: Path,
|
path: Path,
|
||||||
@ -13,14 +15,14 @@ pub enum NodeOperation {
|
|||||||
#[serde(rename = "oldAttributes")]
|
#[serde(rename = "oldAttributes")]
|
||||||
old_attributes: NodeAttributes,
|
old_attributes: NodeAttributes,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[serde(rename = "delete")]
|
#[serde(rename = "delete")]
|
||||||
Delete { path: Path, nodes: Vec<Node> },
|
Delete { path: Path, nodes: Vec<Node> },
|
||||||
#[serde(rename = "text-edit")]
|
|
||||||
TextEdit {
|
#[serde(rename = "edit-body")]
|
||||||
path: Path,
|
#[serde(serialize_with = "serialize_edit_body")]
|
||||||
delta: TextDelta,
|
// #[serde(deserialize_with = "operation_serde::deserialize_edit_body")]
|
||||||
inverted: TextDelta,
|
EditBody { path: Path, changeset: NodeBodyChangeset },
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeOperation {
|
impl NodeOperation {
|
||||||
@ -29,7 +31,7 @@ impl NodeOperation {
|
|||||||
NodeOperation::Insert { path, .. } => path,
|
NodeOperation::Insert { path, .. } => path,
|
||||||
NodeOperation::Update { path, .. } => path,
|
NodeOperation::Update { path, .. } => path,
|
||||||
NodeOperation::Delete { path, .. } => path,
|
NodeOperation::Delete { path, .. } => path,
|
||||||
NodeOperation::TextEdit { path, .. } => path,
|
NodeOperation::EditBody { path, .. } => path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn invert(&self) -> NodeOperation {
|
pub fn invert(&self) -> NodeOperation {
|
||||||
@ -51,10 +53,9 @@ impl NodeOperation {
|
|||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
nodes: nodes.clone(),
|
nodes: nodes.clone(),
|
||||||
},
|
},
|
||||||
NodeOperation::TextEdit { path, delta, inverted } => NodeOperation::TextEdit {
|
NodeOperation::EditBody { path, changeset: body } => NodeOperation::EditBody {
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
delta: inverted.clone(),
|
changeset: body.inverted(),
|
||||||
inverted: delta.clone(),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,10 +78,9 @@ impl NodeOperation {
|
|||||||
path,
|
path,
|
||||||
nodes: nodes.clone(),
|
nodes: nodes.clone(),
|
||||||
},
|
},
|
||||||
NodeOperation::TextEdit { delta, inverted, .. } => NodeOperation::TextEdit {
|
NodeOperation::EditBody { path, changeset: body } => NodeOperation::EditBody {
|
||||||
path,
|
path: path.clone(),
|
||||||
delta: delta.clone(),
|
changeset: body.clone(),
|
||||||
inverted: inverted.clone(),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,35 +101,27 @@ impl NodeOperation {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::core::{Delta, Node, NodeAttributes, NodeOperation, Path};
|
use crate::core::{Node, NodeAttributes, NodeBodyChangeset, NodeBuilder, NodeOperation, Path, TextDelta};
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialize_insert_operation() {
|
fn test_serialize_insert_operation() {
|
||||||
let insert = NodeOperation::Insert {
|
let insert = NodeOperation::Insert {
|
||||||
path: Path(vec![0, 1]),
|
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();
|
let result = serde_json::to_string(&insert).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(result, r#"{"op":"insert","path":[0,1],"nodes":[{"type":"text"}]}"#);
|
||||||
result,
|
|
||||||
r#"{"op":"insert","path":[0,1],"nodes":[{"type":"text","attributes":{}}]}"#
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialize_insert_sub_trees() {
|
fn test_serialize_insert_sub_trees() {
|
||||||
let insert = NodeOperation::Insert {
|
let insert = NodeOperation::Insert {
|
||||||
path: Path(vec![0, 1]),
|
path: Path(vec![0, 1]),
|
||||||
nodes: vec![Node {
|
nodes: vec![NodeBuilder::new("text").add_node(Node::new("text".to_owned())).build()],
|
||||||
note_type: "text".into(),
|
|
||||||
attributes: NodeAttributes::new(),
|
|
||||||
delta: None,
|
|
||||||
children: vec![Node::new("text")],
|
|
||||||
}],
|
|
||||||
};
|
};
|
||||||
let result = serde_json::to_string(&insert).unwrap();
|
let result = serde_json::to_string(&insert).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
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]
|
#[test]
|
||||||
fn test_serialize_text_edit_operation() {
|
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]),
|
path: Path(vec![0, 1]),
|
||||||
delta: Delta::new(),
|
changeset,
|
||||||
inverted: Delta::new(),
|
|
||||||
};
|
};
|
||||||
let result = serde_json::to_string(&insert).unwrap();
|
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
shared-lib/lib-ot/src/core/document/operation_serde.rs
Normal file
29
shared-lib/lib-ot/src/core/document/operation_serde.rs
Normal file
@ -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!()
|
||||||
|
// }
|
@ -127,9 +127,9 @@ impl<'a> TransactionBuilder<'a> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Node {
|
Node {
|
||||||
note_type: node_data.node_type.clone(),
|
node_type: node_data.node_type.clone(),
|
||||||
attributes: node_data.attributes.clone(),
|
attributes: node_data.attributes.clone(),
|
||||||
delta: node_data.delta.clone(),
|
body: node_data.body.clone(),
|
||||||
children,
|
children,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::node::script::NodeScript::*;
|
use crate::node::script::NodeScript::*;
|
||||||
use crate::node::script::NodeTest;
|
use crate::node::script::NodeTest;
|
||||||
use lib_ot::core::{Node, NodeAttributes, Path};
|
use lib_ot::core::{Node, NodeBuilder, Path};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn node_insert_test() {
|
fn node_insert_test() {
|
||||||
@ -23,12 +23,7 @@ fn node_insert_test() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn node_insert_node_with_children_test() {
|
fn node_insert_node_with_children_test() {
|
||||||
let mut test = NodeTest::new();
|
let mut test = NodeTest::new();
|
||||||
let inserted_node = Node {
|
let inserted_node = NodeBuilder::new("text").add_node(Node::new("image")).build();
|
||||||
note_type: "text".into(),
|
|
||||||
attributes: NodeAttributes::new(),
|
|
||||||
delta: None,
|
|
||||||
children: vec![Node::new("image")],
|
|
||||||
};
|
|
||||||
let path: Path = 0.into();
|
let path: Path = 0.into();
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
InsertNode {
|
InsertNode {
|
||||||
|
Loading…
Reference in New Issue
Block a user