use crate::core::{Body, Changeset, NodeData, OperationTransform, Path}; use crate::errors::OTError; use serde::{Deserialize, Serialize}; use std::sync::Arc; #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "op")] pub enum NodeOperation { #[serde(rename = "insert")] Insert { path: Path, nodes: Vec }, #[serde(rename = "update")] Update { path: Path, changeset: Changeset }, #[serde(rename = "delete")] Delete { path: Path, nodes: Vec }, } impl NodeOperation { pub fn get_path(&self) -> &Path { match self { NodeOperation::Insert { path, .. } => path, NodeOperation::Delete { path, .. } => path, NodeOperation::Update { path, .. } => path, } } pub fn get_mut_path(&mut self) -> &mut Path { match self { NodeOperation::Insert { path, .. } => path, NodeOperation::Delete { path, .. } => path, NodeOperation::Update { path, .. } => path, } } pub fn is_update_delta(&self) -> bool { match self { NodeOperation::Insert { .. } => false, NodeOperation::Update { path: _, changeset } => changeset.is_delta(), NodeOperation::Delete { .. } => false, } } pub fn is_update_attribute(&self) -> bool { match self { NodeOperation::Insert { .. } => false, NodeOperation::Update { path: _, changeset } => changeset.is_attribute(), NodeOperation::Delete { .. } => false, } } pub fn is_insert(&self) -> bool { match self { NodeOperation::Insert { .. } => true, NodeOperation::Update { .. } => false, NodeOperation::Delete { .. } => false, } } pub fn can_compose(&self, other: &NodeOperation) -> bool { if self.get_path() != other.get_path() { return false; } if self.is_update_delta() && other.is_update_delta() { return true; } if self.is_update_attribute() && other.is_update_attribute() { return true; } if self.is_insert() && other.is_update_delta() { return true; } false } pub fn compose(&mut self, other: &NodeOperation) -> Result<(), OTError> { match (self, other) { ( NodeOperation::Insert { path: _, nodes }, NodeOperation::Update { path: _other_path, changeset, }, ) => { match changeset { Changeset::Delta { delta, inverted: _ } => { if let Body::Delta(old_delta) = &mut nodes.last_mut().unwrap().body { let new_delta = old_delta.compose(delta)?; *old_delta = new_delta; } }, Changeset::Attributes { new: _, old: _ } => { return Err(OTError::compose().context("Can't compose the attributes changeset")); }, } Ok(()) }, ( NodeOperation::Update { path: _, changeset }, NodeOperation::Update { path: _, changeset: other_changeset, }, ) => changeset.compose(other_changeset), (_left, _right) => Err(OTError::compose().context("Can't compose the operation")), } } pub fn inverted(&self) -> NodeOperation { match self { NodeOperation::Insert { path, nodes } => NodeOperation::Delete { path: path.clone(), nodes: nodes.clone(), }, NodeOperation::Delete { path, nodes } => NodeOperation::Insert { path: path.clone(), nodes: nodes.clone(), }, NodeOperation::Update { path, changeset: body, } => NodeOperation::Update { path: path.clone(), changeset: body.inverted(), }, } } /// Make the `other` operation can be applied to the version after applying the `self` operation. /// The semantics of transform is used when editing conflicts occur, which is often determined by the version id。 /// For example, if the inserted position has been acquired by others, then it's needed to do the transform to /// make sure the inserted position is right. /// /// # Arguments /// /// * `other`: The operation that is going to be transformed /// /// # Examples /// /// ``` /// use lib_ot::core::{NodeDataBuilder, NodeOperation, Path}; /// let node_1 = NodeDataBuilder::new("text_1").build(); /// let node_2 = NodeDataBuilder::new("text_2").build(); /// /// let op_1 = NodeOperation::Insert { /// path: Path(vec![0, 1]), /// nodes: vec![node_1], /// }; /// /// let mut op_2 = NodeOperation::Insert { /// path: Path(vec![0, 1]), /// nodes: vec![node_2], /// }; /// /// assert_eq!(serde_json::to_string(&op_2).unwrap(), r#"{"op":"insert","path":[0,1],"nodes":[{"type":"text_2"}]}"#); /// /// op_1.transform(&mut op_2); /// assert_eq!(serde_json::to_string(&op_2).unwrap(), r#"{"op":"insert","path":[0,2],"nodes":[{"type":"text_2"}]}"#); /// assert_eq!(serde_json::to_string(&op_1).unwrap(), r#"{"op":"insert","path":[0,1],"nodes":[{"type":"text_1"}]}"#); /// ``` pub fn transform(&self, other: &mut NodeOperation) { match self { NodeOperation::Insert { path, nodes } => { let new_path = path.transform(other.get_path(), nodes.len()); *other.get_mut_path() = new_path; }, NodeOperation::Delete { path, nodes } => { let new_path = path.transform(other.get_path(), nodes.len()); *other.get_mut_path() = new_path; }, _ => { // Only insert/delete will change the path. }, } } } type OperationIndexMap = Vec>; #[derive(Debug, Clone, Default)] pub struct NodeOperations { inner: OperationIndexMap, } impl NodeOperations { pub fn new() -> Self { Self::default() } pub fn from_operations(operations: Vec) -> Self { let mut ops = Self::new(); for op in operations { ops.push_op(op) } ops } pub fn from_bytes(bytes: Vec) -> Result { let operation_list = serde_json::from_slice(&bytes).map_err(|err| OTError::serde().context(err))?; Ok(operation_list) } pub fn to_bytes(&self) -> Result, OTError> { let bytes = serde_json::to_vec(self).map_err(|err| OTError::serde().context(err))?; Ok(bytes) } pub fn values(&self) -> &Vec> { &self.inner } pub fn values_mut(&mut self) -> &mut Vec> { &mut self.inner } pub fn len(&self) -> usize { self.values().len() } pub fn is_empty(&self) -> bool { self.inner.is_empty() } pub fn into_inner(self) -> Vec> { self.inner } pub fn push_op>>(&mut self, other: T) { let other = other.into(); if let Some(last_operation) = self.inner.last_mut() { if last_operation.can_compose(&other) { let mut_operation = Arc::make_mut(last_operation); if mut_operation.compose(&other).is_ok() { return; } } } // If the passed-in operation can't be composed, then append it to the end. self.inner.push(other); } pub fn compose(&mut self, other: NodeOperations) { for operation in other.values() { self.push_op(operation.clone()); } } pub fn inverted(&self) -> Self { let mut operations = Self::new(); for operation in self.values() { operations.push_op(operation.inverted()); } operations } } impl std::convert::From> for NodeOperations { fn from(operations: Vec) -> Self { Self::from_operations(operations) } } impl std::convert::From for NodeOperations { fn from(operation: NodeOperation) -> Self { Self::from_operations(vec![operation]) } }