mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: introduce error to apply method
This commit is contained in:
parent
ef185cd5d5
commit
b0bafff22c
7
frontend/rust-lib/Cargo.lock
generated
7
frontend/rust-lib/Cargo.lock
generated
@ -1618,6 +1618,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indextree"
|
||||||
|
version = "4.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42b4b46b3311ebd8e5cd44f6b03b36e0f48a70552cf6b036afcebc5626794066"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "instant"
|
name = "instant"
|
||||||
version = "0.1.12"
|
version = "0.1.12"
|
||||||
@ -1766,6 +1772,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
|
"indextree",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"md5",
|
"md5",
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use crate::core::document::position::Position;
|
use crate::core::document::position::Position;
|
||||||
use crate::core::{
|
use crate::core::{DocumentOperation, NodeAttributes, NodeData, OperationTransform, TextDelta, Transaction};
|
||||||
DeleteOperation, DocumentOperation, InsertOperation, NodeAttributes, NodeData, TextEditOperation, Transaction,
|
use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
|
||||||
UpdateOperation,
|
|
||||||
};
|
|
||||||
use indextree::{Arena, NodeId};
|
use indextree::{Arena, NodeId};
|
||||||
|
|
||||||
pub struct DocumentTree {
|
pub struct DocumentTree {
|
||||||
@ -86,42 +84,48 @@ impl DocumentTree {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply(&mut self, transaction: Transaction) {
|
pub fn apply(&mut self, transaction: Transaction) -> Result<(), OTError> {
|
||||||
for op in &transaction.operations {
|
for op in &transaction.operations {
|
||||||
self.apply_op(op);
|
self.apply_op(op)?;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_op(&mut self, op: &DocumentOperation) {
|
fn apply_op(&mut self, op: &DocumentOperation) -> Result<(), OTError> {
|
||||||
match op {
|
match op {
|
||||||
DocumentOperation::Insert(op) => self.apply_insert(op),
|
DocumentOperation::Insert { path, nodes } => self.apply_insert(path, nodes),
|
||||||
DocumentOperation::Update(op) => self.apply_update(op),
|
DocumentOperation::Update { path, attributes, .. } => self.apply_update(path, attributes),
|
||||||
DocumentOperation::Delete(op) => self.apply_delete(op),
|
DocumentOperation::Delete { path, nodes } => self.apply_delete(path, nodes.len()),
|
||||||
DocumentOperation::TextEdit(op) => self.apply_text_edit(op),
|
DocumentOperation::TextEdit { path, delta, .. } => self.apply_text_edit(path, delta),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_insert(&mut self, op: &InsertOperation) {
|
fn apply_insert(&mut self, path: &Position, nodes: &Vec<NodeData>) -> Result<(), OTError> {
|
||||||
let parent_path = &op.path.0[0..(op.path.0.len() - 1)];
|
let parent_path = &path.0[0..(path.0.len() - 1)];
|
||||||
let last_index = op.path.0[op.path.0.len() - 1];
|
let last_index = path.0[path.0.len() - 1];
|
||||||
let parent_node = self.node_at_path(&Position(parent_path.to_vec()));
|
let parent_node = self
|
||||||
if let Some(parent_node) = parent_node {
|
.node_at_path(&Position(parent_path.to_vec()))
|
||||||
let mut inserted_nodes = Vec::new();
|
.ok_or(ErrorBuilder::new(OTErrorCode::PathNotFound).build())?;
|
||||||
|
let mut inserted_nodes = Vec::new();
|
||||||
|
|
||||||
for node in &op.nodes {
|
for node in nodes {
|
||||||
inserted_nodes.push(self.arena.new_node(node.clone()));
|
inserted_nodes.push(self.arena.new_node(node.clone()));
|
||||||
}
|
|
||||||
|
|
||||||
self.insert_child_at_index(parent_node, last_index, &inserted_nodes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.insert_child_at_index(parent_node, last_index, &inserted_nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_child_at_index(&mut self, parent: NodeId, index: usize, insert_children: &[NodeId]) {
|
fn insert_child_at_index(
|
||||||
|
&mut self,
|
||||||
|
parent: NodeId,
|
||||||
|
index: usize,
|
||||||
|
insert_children: &[NodeId],
|
||||||
|
) -> Result<(), OTError> {
|
||||||
if index == 0 && parent.children(&self.arena).next().is_none() {
|
if index == 0 && parent.children(&self.arena).next().is_none() {
|
||||||
for id in insert_children {
|
for id in insert_children {
|
||||||
parent.append(*id, &mut self.arena);
|
parent.append(*id, &mut self.arena);
|
||||||
}
|
}
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let children_length = parent.children(&self.arena).fold(0, |counter, _| counter + 1);
|
let children_length = parent.children(&self.arena).fold(0, |counter, _| counter + 1);
|
||||||
@ -130,32 +134,72 @@ impl DocumentTree {
|
|||||||
for id in insert_children {
|
for id in insert_children {
|
||||||
parent.append(*id, &mut self.arena);
|
parent.append(*id, &mut self.arena);
|
||||||
}
|
}
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let node_to_insert = self.child_at_index_of_path(parent, index).unwrap();
|
let node_to_insert = self
|
||||||
|
.child_at_index_of_path(parent, index)
|
||||||
|
.ok_or(ErrorBuilder::new(OTErrorCode::PathNotFound).build())?;
|
||||||
|
|
||||||
for id in insert_children {
|
for id in insert_children {
|
||||||
node_to_insert.insert_before(*id, &mut self.arena);
|
node_to_insert.insert_before(*id, &mut self.arena);
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_update(&self, op: &UpdateOperation) {
|
fn apply_update(&mut self, path: &Position, attributes: &NodeAttributes) -> Result<(), OTError> {
|
||||||
let update_node = self.node_at_path(&op.path).unwrap();
|
let update_node = self
|
||||||
let node_data = self.arena.get(update_node).unwrap();
|
.node_at_path(path)
|
||||||
let new_attributes = {
|
.ok_or(ErrorBuilder::new(OTErrorCode::PathNotFound).build())?;
|
||||||
let old_attributes = node_data.get().attributes.borrow();
|
let node_data = self.arena.get_mut(update_node).unwrap();
|
||||||
NodeAttributes::compose(&old_attributes, &op.attributes)
|
// let new_node = NodeData {
|
||||||
|
// ..node_data.get().clone()
|
||||||
|
// attributes:
|
||||||
|
// };
|
||||||
|
let new_node = {
|
||||||
|
let old_attributes = &node_data.get().attributes;
|
||||||
|
let new_attributes = NodeAttributes::compose(&old_attributes, attributes);
|
||||||
|
NodeData {
|
||||||
|
attributes: new_attributes,
|
||||||
|
..node_data.get().clone()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
node_data.get().attributes.replace(new_attributes);
|
*node_data.get_mut() = new_node;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_delete(&mut self, op: &DeleteOperation) {
|
fn apply_delete(&mut self, path: &Position, len: usize) -> Result<(), OTError> {
|
||||||
let update_node = self.node_at_path(&op.path).unwrap();
|
let mut update_node = self
|
||||||
update_node.remove_subtree(&mut self.arena);
|
.node_at_path(path)
|
||||||
|
.ok_or(ErrorBuilder::new(OTErrorCode::PathNotFound).build())?;
|
||||||
|
for _ in 0..len {
|
||||||
|
let next = update_node.following_siblings(&self.arena).next();
|
||||||
|
update_node.remove_subtree(&mut self.arena);
|
||||||
|
if let Some(next_id) = next {
|
||||||
|
update_node = next_id;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_text_edit(&self, _op: &TextEditOperation) {
|
fn apply_text_edit(&mut self, path: &Position, delta: &TextDelta) -> Result<(), OTError> {
|
||||||
unimplemented!()
|
let edit_node = self
|
||||||
|
.node_at_path(path)
|
||||||
|
.ok_or(ErrorBuilder::new(OTErrorCode::PathNotFound).build())?;
|
||||||
|
let node_data = self.arena.get_mut(edit_node).unwrap();
|
||||||
|
let new_delta = if let Some(old_delta) = &node_data.get().delta {
|
||||||
|
Some(old_delta.compose(delta)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
if let Some(new_delta) = new_delta {
|
||||||
|
*node_data.get_mut() = NodeData {
|
||||||
|
delta: Some(new_delta),
|
||||||
|
..node_data.get().clone()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,102 +3,98 @@ use crate::core::{NodeAttributes, NodeData, TextDelta};
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum DocumentOperation {
|
pub enum DocumentOperation {
|
||||||
Insert(InsertOperation),
|
Insert {
|
||||||
Update(UpdateOperation),
|
path: Position,
|
||||||
Delete(DeleteOperation),
|
nodes: Vec<NodeData>,
|
||||||
TextEdit(TextEditOperation),
|
},
|
||||||
|
Update {
|
||||||
|
path: Position,
|
||||||
|
attributes: NodeAttributes,
|
||||||
|
old_attributes: NodeAttributes,
|
||||||
|
},
|
||||||
|
Delete {
|
||||||
|
path: Position,
|
||||||
|
nodes: Vec<NodeData>,
|
||||||
|
},
|
||||||
|
TextEdit {
|
||||||
|
path: Position,
|
||||||
|
delta: TextDelta,
|
||||||
|
inverted: TextDelta,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocumentOperation {
|
impl DocumentOperation {
|
||||||
pub fn path(&self) -> &Position {
|
pub fn path(&self) -> &Position {
|
||||||
match self {
|
match self {
|
||||||
DocumentOperation::Insert(insert) => &insert.path,
|
DocumentOperation::Insert { path, .. } => path,
|
||||||
DocumentOperation::Update(update) => &update.path,
|
DocumentOperation::Update { path, .. } => path,
|
||||||
DocumentOperation::Delete(delete) => &delete.path,
|
DocumentOperation::Delete { path, .. } => path,
|
||||||
DocumentOperation::TextEdit(text_edit) => &text_edit.path,
|
DocumentOperation::TextEdit { path, .. } => path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn invert(&self) -> DocumentOperation {
|
pub fn invert(&self) -> DocumentOperation {
|
||||||
match self {
|
match self {
|
||||||
DocumentOperation::Insert(insert_operation) => DocumentOperation::Delete(DeleteOperation {
|
DocumentOperation::Insert { path, nodes } => DocumentOperation::Delete {
|
||||||
path: insert_operation.path.clone(),
|
path: path.clone(),
|
||||||
nodes: insert_operation.nodes.clone(),
|
nodes: nodes.clone(),
|
||||||
}),
|
},
|
||||||
DocumentOperation::Update(update_operation) => DocumentOperation::Update(UpdateOperation {
|
DocumentOperation::Update {
|
||||||
path: update_operation.path.clone(),
|
path,
|
||||||
attributes: update_operation.old_attributes.clone(),
|
attributes,
|
||||||
old_attributes: update_operation.attributes.clone(),
|
old_attributes,
|
||||||
}),
|
} => DocumentOperation::Update {
|
||||||
DocumentOperation::Delete(delete_operation) => DocumentOperation::Insert(InsertOperation {
|
path: path.clone(),
|
||||||
path: delete_operation.path.clone(),
|
attributes: old_attributes.clone(),
|
||||||
nodes: delete_operation.nodes.clone(),
|
old_attributes: attributes.clone(),
|
||||||
}),
|
},
|
||||||
DocumentOperation::TextEdit(text_edit_operation) => DocumentOperation::TextEdit(TextEditOperation {
|
DocumentOperation::Delete { path, nodes } => DocumentOperation::Insert {
|
||||||
path: text_edit_operation.path.clone(),
|
path: path.clone(),
|
||||||
delta: text_edit_operation.inverted.clone(),
|
nodes: nodes.clone(),
|
||||||
inverted: text_edit_operation.delta.clone(),
|
},
|
||||||
}),
|
DocumentOperation::TextEdit { path, delta, inverted } => DocumentOperation::TextEdit {
|
||||||
|
path: path.clone(),
|
||||||
|
delta: inverted.clone(),
|
||||||
|
inverted: delta.clone(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn clone_with_new_path(&self, path: Position) -> DocumentOperation {
|
pub fn clone_with_new_path(&self, path: Position) -> DocumentOperation {
|
||||||
match self {
|
match self {
|
||||||
DocumentOperation::Insert(insert) => DocumentOperation::Insert(InsertOperation {
|
DocumentOperation::Insert { nodes, .. } => DocumentOperation::Insert {
|
||||||
path,
|
path,
|
||||||
nodes: insert.nodes.clone(),
|
nodes: nodes.clone(),
|
||||||
}),
|
},
|
||||||
DocumentOperation::Update(update) => DocumentOperation::Update(UpdateOperation {
|
DocumentOperation::Update {
|
||||||
|
attributes,
|
||||||
|
old_attributes,
|
||||||
|
..
|
||||||
|
} => DocumentOperation::Update {
|
||||||
path,
|
path,
|
||||||
attributes: update.attributes.clone(),
|
attributes: attributes.clone(),
|
||||||
old_attributes: update.old_attributes.clone(),
|
old_attributes: old_attributes.clone(),
|
||||||
}),
|
},
|
||||||
DocumentOperation::Delete(delete) => DocumentOperation::Delete(DeleteOperation {
|
DocumentOperation::Delete { nodes, .. } => DocumentOperation::Delete {
|
||||||
path,
|
path,
|
||||||
nodes: delete.nodes.clone(),
|
nodes: nodes.clone(),
|
||||||
}),
|
},
|
||||||
DocumentOperation::TextEdit(text_edit) => DocumentOperation::TextEdit(TextEditOperation {
|
DocumentOperation::TextEdit { delta, inverted, .. } => DocumentOperation::TextEdit {
|
||||||
path,
|
path,
|
||||||
delta: text_edit.delta.clone(),
|
delta: delta.clone(),
|
||||||
inverted: text_edit.inverted.clone(),
|
inverted: inverted.clone(),
|
||||||
}),
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn transform(a: &DocumentOperation, b: &DocumentOperation) -> DocumentOperation {
|
pub fn transform(a: &DocumentOperation, b: &DocumentOperation) -> DocumentOperation {
|
||||||
match a {
|
match a {
|
||||||
DocumentOperation::Insert(insert_op) => {
|
DocumentOperation::Insert { path: a_path, nodes } => {
|
||||||
let new_path = Position::transform(a.path(), b.path(), insert_op.nodes.len() as i64);
|
let new_path = Position::transform(a_path, b.path(), nodes.len() as i64);
|
||||||
b.clone_with_new_path(new_path)
|
b.clone_with_new_path(new_path)
|
||||||
}
|
}
|
||||||
DocumentOperation::Delete(delete_op) => {
|
DocumentOperation::Delete { path: a_path, nodes } => {
|
||||||
let new_path = Position::transform(a.path(), b.path(), delete_op.nodes.len() as i64);
|
let new_path = Position::transform(a_path, b.path(), nodes.len() as i64);
|
||||||
b.clone_with_new_path(new_path)
|
b.clone_with_new_path(new_path)
|
||||||
}
|
}
|
||||||
_ => b.clone(),
|
_ => b.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct InsertOperation {
|
|
||||||
pub path: Position,
|
|
||||||
pub nodes: Vec<NodeData>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct UpdateOperation {
|
|
||||||
pub path: Position,
|
|
||||||
pub attributes: NodeAttributes,
|
|
||||||
pub old_attributes: NodeAttributes,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct DeleteOperation {
|
|
||||||
pub path: Position,
|
|
||||||
pub nodes: Vec<NodeData>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TextEditOperation {
|
|
||||||
pub path: Position,
|
|
||||||
pub delta: TextDelta,
|
|
||||||
pub inverted: TextDelta,
|
|
||||||
}
|
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
use crate::core::{NodeAttributes, TextDelta};
|
use crate::core::{NodeAttributes, TextDelta};
|
||||||
use std::cell::RefCell;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct NodeData {
|
pub struct NodeData {
|
||||||
pub node_type: String,
|
pub node_type: String,
|
||||||
pub attributes: RefCell<NodeAttributes>,
|
pub attributes: NodeAttributes,
|
||||||
pub delta: RefCell<Option<TextDelta>>,
|
pub delta: Option<TextDelta>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeData {
|
impl NodeData {
|
||||||
pub fn new(node_type: &str) -> NodeData {
|
pub fn new(node_type: &str) -> NodeData {
|
||||||
NodeData {
|
NodeData {
|
||||||
node_type: node_type.into(),
|
node_type: node_type.into(),
|
||||||
attributes: RefCell::new(NodeAttributes::new()),
|
attributes: NodeAttributes::new(),
|
||||||
delta: RefCell::new(None),
|
delta: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
use crate::core::document::position::Position;
|
use crate::core::document::position::Position;
|
||||||
use crate::core::{
|
use crate::core::{DocumentOperation, DocumentTree, NodeAttributes, NodeData};
|
||||||
DeleteOperation, DocumentOperation, DocumentTree, InsertOperation, NodeAttributes, NodeData, UpdateOperation,
|
|
||||||
};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
@ -28,10 +26,10 @@ impl<'a> TransactionBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_nodes_at_path(&mut self, path: &Position, nodes: &[NodeData]) {
|
pub fn insert_nodes_at_path(&mut self, path: &Position, nodes: &[NodeData]) {
|
||||||
self.push(DocumentOperation::Insert(InsertOperation {
|
self.push(DocumentOperation::Insert {
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
nodes: nodes.to_vec(),
|
nodes: nodes.to_vec(),
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_attributes_at_path(&mut self, path: &Position, attributes: HashMap<String, Option<String>>) {
|
pub fn update_attributes_at_path(&mut self, path: &Position, attributes: HashMap<String, Option<String>>) {
|
||||||
@ -40,7 +38,7 @@ impl<'a> TransactionBuilder<'a> {
|
|||||||
let node_data = self.document.arena.get(node).unwrap().get();
|
let node_data = self.document.arena.get(node).unwrap().get();
|
||||||
|
|
||||||
for key in attributes.keys() {
|
for key in attributes.keys() {
|
||||||
let old_attrs = node_data.attributes.borrow();
|
let old_attrs = &node_data.attributes;
|
||||||
let old_value = match old_attrs.0.get(key.as_str()) {
|
let old_value = match old_attrs.0.get(key.as_str()) {
|
||||||
Some(value) => value.clone(),
|
Some(value) => value.clone(),
|
||||||
None => None,
|
None => None,
|
||||||
@ -48,11 +46,11 @@ impl<'a> TransactionBuilder<'a> {
|
|||||||
old_attributes.insert(key.clone(), old_value);
|
old_attributes.insert(key.clone(), old_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.push(DocumentOperation::Update(UpdateOperation {
|
self.push(DocumentOperation::Update {
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
attributes: NodeAttributes(attributes),
|
attributes: NodeAttributes(attributes),
|
||||||
old_attributes: NodeAttributes(old_attributes),
|
old_attributes: NodeAttributes(old_attributes),
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_node_at_path(&mut self, path: &Position) {
|
pub fn delete_node_at_path(&mut self, path: &Position) {
|
||||||
@ -69,10 +67,10 @@ impl<'a> TransactionBuilder<'a> {
|
|||||||
node = node.following_siblings(&self.document.arena).next().unwrap();
|
node = node.following_siblings(&self.document.arena).next().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.operations.push(DocumentOperation::Delete(DeleteOperation {
|
self.operations.push(DocumentOperation::Delete {
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
nodes: deleted_nodes,
|
nodes: deleted_nodes,
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, op: DocumentOperation) {
|
pub fn push(&mut self, op: DocumentOperation) {
|
||||||
|
@ -60,7 +60,7 @@ impl std::convert::From<Utf8Error> for OTError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum OTErrorCode {
|
pub enum OTErrorCode {
|
||||||
IncompatibleLength,
|
IncompatibleLength,
|
||||||
ApplyInsertFail,
|
ApplyInsertFail,
|
||||||
@ -74,6 +74,7 @@ pub enum OTErrorCode {
|
|||||||
DuplicatedRevision,
|
DuplicatedRevision,
|
||||||
RevisionIDConflict,
|
RevisionIDConflict,
|
||||||
Internal,
|
Internal,
|
||||||
|
PathNotFound,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ErrorBuilder {
|
pub struct ErrorBuilder {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use lib_ot::core::{DocumentTree, NodeData, Position, TransactionBuilder};
|
use lib_ot::core::{DocumentTree, NodeData, Position, TransactionBuilder};
|
||||||
|
use lib_ot::errors::OTErrorCode;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -15,7 +16,7 @@ fn test_documents() {
|
|||||||
tb.insert_nodes_at_path(&vec![0].into(), &vec![NodeData::new("text")]);
|
tb.insert_nodes_at_path(&vec![0].into(), &vec![NodeData::new("text")]);
|
||||||
tb.finalize()
|
tb.finalize()
|
||||||
};
|
};
|
||||||
document.apply(transaction);
|
document.apply(transaction).unwrap();
|
||||||
|
|
||||||
assert!(document.node_at_path(&vec![0].into()).is_some());
|
assert!(document.node_at_path(&vec![0].into()).is_some());
|
||||||
let node = document.node_at_path(&vec![0].into()).unwrap();
|
let node = document.node_at_path(&vec![0].into()).unwrap();
|
||||||
@ -30,14 +31,14 @@ fn test_documents() {
|
|||||||
);
|
);
|
||||||
tb.finalize()
|
tb.finalize()
|
||||||
};
|
};
|
||||||
document.apply(transaction);
|
document.apply(transaction).unwrap();
|
||||||
|
|
||||||
let transaction = {
|
let transaction = {
|
||||||
let mut tb = TransactionBuilder::new(&document);
|
let mut tb = TransactionBuilder::new(&document);
|
||||||
tb.delete_node_at_path(&vec![0].into());
|
tb.delete_node_at_path(&vec![0].into());
|
||||||
tb.finalize()
|
tb.finalize()
|
||||||
};
|
};
|
||||||
document.apply(transaction);
|
document.apply(transaction).unwrap();
|
||||||
assert!(document.node_at_path(&vec![0].into()).is_none());
|
assert!(document.node_at_path(&vec![0].into()).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,14 +52,14 @@ fn test_inserts_nodes() {
|
|||||||
tb.insert_nodes_at_path(&vec![2].into(), &vec![NodeData::new("text")]);
|
tb.insert_nodes_at_path(&vec![2].into(), &vec![NodeData::new("text")]);
|
||||||
tb.finalize()
|
tb.finalize()
|
||||||
};
|
};
|
||||||
document.apply(transaction);
|
document.apply(transaction).unwrap();
|
||||||
|
|
||||||
let transaction = {
|
let transaction = {
|
||||||
let mut tb = TransactionBuilder::new(&document);
|
let mut tb = TransactionBuilder::new(&document);
|
||||||
tb.insert_nodes_at_path(&vec![1].into(), &vec![NodeData::new("text")]);
|
tb.insert_nodes_at_path(&vec![1].into(), &vec![NodeData::new("text")]);
|
||||||
tb.finalize()
|
tb.finalize()
|
||||||
};
|
};
|
||||||
document.apply(transaction);
|
document.apply(transaction).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -71,18 +72,18 @@ fn test_update_nodes() {
|
|||||||
tb.insert_nodes_at_path(&vec![2].into(), &vec![NodeData::new("text")]);
|
tb.insert_nodes_at_path(&vec![2].into(), &vec![NodeData::new("text")]);
|
||||||
tb.finalize()
|
tb.finalize()
|
||||||
};
|
};
|
||||||
document.apply(transaction);
|
document.apply(transaction).unwrap();
|
||||||
|
|
||||||
let transaction = {
|
let transaction = {
|
||||||
let mut tb = TransactionBuilder::new(&document);
|
let mut tb = TransactionBuilder::new(&document);
|
||||||
tb.update_attributes_at_path(&vec![1].into(), HashMap::from([("bolded".into(), Some("true".into()))]));
|
tb.update_attributes_at_path(&vec![1].into(), HashMap::from([("bolded".into(), Some("true".into()))]));
|
||||||
tb.finalize()
|
tb.finalize()
|
||||||
};
|
};
|
||||||
document.apply(transaction);
|
document.apply(transaction).unwrap();
|
||||||
|
|
||||||
let node = document.node_at_path(&Position(vec![1])).unwrap();
|
let node = document.node_at_path(&Position(vec![1])).unwrap();
|
||||||
let node_data = document.arena.get(node).unwrap().get();
|
let node_data = document.arena.get(node).unwrap().get();
|
||||||
let is_bold = node_data.attributes.borrow().0.get("bolded").unwrap().clone();
|
let is_bold = node_data.attributes.0.get("bolded").unwrap().clone();
|
||||||
assert_eq!(is_bold.unwrap(), "true");
|
assert_eq!(is_bold.unwrap(), "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,15 +97,28 @@ fn test_delete_nodes() {
|
|||||||
tb.insert_nodes_at_path(&vec![2].into(), &vec![NodeData::new("text")]);
|
tb.insert_nodes_at_path(&vec![2].into(), &vec![NodeData::new("text")]);
|
||||||
tb.finalize()
|
tb.finalize()
|
||||||
};
|
};
|
||||||
document.apply(transaction);
|
document.apply(transaction).unwrap();
|
||||||
|
|
||||||
let transaction = {
|
let transaction = {
|
||||||
let mut tb = TransactionBuilder::new(&document);
|
let mut tb = TransactionBuilder::new(&document);
|
||||||
tb.delete_node_at_path(&Position(vec![1]));
|
tb.delete_node_at_path(&Position(vec![1]));
|
||||||
tb.finalize()
|
tb.finalize()
|
||||||
};
|
};
|
||||||
document.apply(transaction);
|
document.apply(transaction).unwrap();
|
||||||
|
|
||||||
let len = document.root.children(&document.arena).fold(0, |count, _| count + 1);
|
let len = document.root.children(&document.arena).fold(0, |count, _| count + 1);
|
||||||
assert_eq!(len, 2);
|
assert_eq!(len, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_errors() {
|
||||||
|
let mut document = DocumentTree::new();
|
||||||
|
let transaction = {
|
||||||
|
let mut tb = TransactionBuilder::new(&document);
|
||||||
|
tb.insert_nodes_at_path(&vec![0].into(), &vec![NodeData::new("text")]);
|
||||||
|
tb.insert_nodes_at_path(&vec![100].into(), &vec![NodeData::new("text")]);
|
||||||
|
tb.finalize()
|
||||||
|
};
|
||||||
|
let result = document.apply(transaction);
|
||||||
|
assert_eq!(result.err().unwrap().code, OTErrorCode::PathNotFound);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user