check size when applying operations

This commit is contained in:
Nikita Galaiko 2023-03-03 16:06:00 +01:00
parent 887b676acb
commit f78d5b104f
3 changed files with 77 additions and 34 deletions

View File

@ -1,3 +1,4 @@
use anyhow::Result;
use difference::{Changeset, Difference}; use difference::{Changeset, Difference};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -11,13 +12,40 @@ pub enum Operation {
} }
impl Operation { impl Operation {
pub fn apply(&self, text: &mut Vec<char>) { pub fn apply(&self, text: &mut Vec<char>) -> Result<()> {
match self { match self {
Operation::Insert((index, chunk)) => { Operation::Insert((index, chunk)) => {
if *index as usize > text.len() {
Err(anyhow::anyhow!(
"Index out of bounds, {} > {}",
index,
text.len()
))
} else if *index as usize == text.len() {
text.extend(chunk.chars());
Ok(())
} else {
text.splice(*index as usize..*index as usize, chunk.chars()); text.splice(*index as usize..*index as usize, chunk.chars());
Ok(())
}
} }
Operation::Delete((index, len)) => { Operation::Delete((index, len)) => {
if *index as usize > text.len() {
Err(anyhow::anyhow!(
"Index out of bounds, {} > {}",
index,
text.len()
))
} else if *index as usize + *len as usize > text.len() {
Err(anyhow::anyhow!(
"Index + length out of bounds, {} > {}",
index + len,
text.len()
))
} else {
text.splice(*index as usize..(*index + *len) as usize, "".chars()); text.splice(*index as usize..(*index + *len) as usize, "".chars());
Ok(())
}
} }
} }
} }

View File

@ -1,6 +1,6 @@
use std::time::SystemTime;
use super::{deltas, operations}; use super::{deltas, operations};
use anyhow::Result;
use std::time::SystemTime;
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct TextDocument { pub struct TextDocument {
@ -8,20 +8,21 @@ pub struct TextDocument {
deltas: Vec<deltas::Delta>, deltas: Vec<deltas::Delta>,
} }
fn apply_deltas(doc: &mut Vec<char>, deltas: &Vec<deltas::Delta>) { fn apply_deltas(doc: &mut Vec<char>, deltas: &Vec<deltas::Delta>) -> Result<()> {
for delta in deltas { for delta in deltas {
for operation in &delta.operations { for operation in &delta.operations {
operation.apply(doc); operation.apply(doc)?;
} }
} }
Ok(())
} }
impl TextDocument { impl TextDocument {
// creates a new text document from a deltas. // creates a new text document from a deltas.
pub fn from_deltas(deltas: Vec<deltas::Delta>) -> TextDocument { pub fn from_deltas(deltas: Vec<deltas::Delta>) -> Result<TextDocument> {
let mut doc = vec![]; let mut doc = vec![];
apply_deltas(&mut doc, &deltas); apply_deltas(&mut doc, &deltas)?;
TextDocument { doc, deltas } Ok(TextDocument { doc, deltas })
} }
pub fn get_deltas(&self) -> Vec<deltas::Delta> { pub fn get_deltas(&self) -> Vec<deltas::Delta> {
@ -29,18 +30,18 @@ impl TextDocument {
} }
// returns a text document where internal state is seeded with value, and deltas are applied. // returns a text document where internal state is seeded with value, and deltas are applied.
pub fn new(value: &str, deltas: Vec<deltas::Delta>) -> TextDocument { pub fn new(value: &str, deltas: Vec<deltas::Delta>) -> Result<TextDocument> {
let mut all_deltas = vec![deltas::Delta { let mut all_deltas = vec![deltas::Delta {
operations: operations::get_delta_operations("", value), operations: operations::get_delta_operations("", value),
timestamp_ms: 0, timestamp_ms: 0,
}]; }];
all_deltas.append(&mut deltas.clone()); all_deltas.append(&mut deltas.clone());
let mut doc = vec![]; let mut doc = vec![];
apply_deltas(&mut doc, &all_deltas); apply_deltas(&mut doc, &all_deltas)?;
TextDocument { doc, deltas } Ok(TextDocument { doc, deltas })
} }
pub fn update(&mut self, value: &str) -> bool { pub fn update(&mut self, value: &str) -> Result<bool> {
let diffs = operations::get_delta_operations(&self.to_string(), value); let diffs = operations::get_delta_operations(&self.to_string(), value);
let event = deltas::Delta { let event = deltas::Delta {
operations: diffs, operations: diffs,
@ -50,11 +51,11 @@ impl TextDocument {
.as_millis(), .as_millis(),
}; };
if event.operations.len() == 0 { if event.operations.len() == 0 {
return false; return Ok(false);
} }
apply_deltas(&mut self.doc, &vec![event.clone()]); apply_deltas(&mut self.doc, &vec![event.clone()])?;
self.deltas.push(event); self.deltas.push(event);
return true; return Ok(true);
} }
pub fn to_string(&self) -> String { pub fn to_string(&self) -> String {

View File

@ -3,14 +3,18 @@ use crate::deltas::{operations::Operation, text_document::TextDocument, Delta};
#[test] #[test]
fn test_new() { fn test_new() {
let document = TextDocument::new("hello world", vec![]); let document = TextDocument::new("hello world", vec![]);
assert_eq!(document.is_ok(), true);
let document = document.unwrap();
assert_eq!(document.to_string(), "hello world"); assert_eq!(document.to_string(), "hello world");
assert_eq!(document.get_deltas().len(), 0); assert_eq!(document.get_deltas().len(), 0);
} }
#[test] #[test]
fn test_update() { fn test_update() {
let mut document = TextDocument::new("hello world", vec![]); let document = TextDocument::new("hello world", vec![]);
document.update("hello world!"); assert_eq!(document.is_ok(), true);
let mut document = document.unwrap();
document.update("hello world!").unwrap();
assert_eq!(document.to_string(), "hello world!"); assert_eq!(document.to_string(), "hello world!");
assert_eq!(document.get_deltas().len(), 1); assert_eq!(document.get_deltas().len(), 1);
assert_eq!(document.get_deltas()[0].operations.len(), 1); assert_eq!(document.get_deltas()[0].operations.len(), 1);
@ -22,8 +26,10 @@ fn test_update() {
#[test] #[test]
fn test_empty() { fn test_empty() {
let mut document = TextDocument::from_deltas(vec![]); let document = TextDocument::from_deltas(vec![]);
document.update("hello world!"); assert_eq!(document.is_ok(), true);
let mut document = document.unwrap();
document.update("hello world!").unwrap();
assert_eq!(document.to_string(), "hello world!"); assert_eq!(document.to_string(), "hello world!");
assert_eq!(document.get_deltas().len(), 1); assert_eq!(document.get_deltas().len(), 1);
assert_eq!(document.get_deltas()[0].operations.len(), 1); assert_eq!(document.get_deltas()[0].operations.len(), 1);
@ -52,14 +58,18 @@ fn test_from_deltas() {
], ],
}, },
]); ]);
assert_eq!(document.is_ok(), true);
let document = document.unwrap();
assert_eq!(document.to_string(), "held!"); assert_eq!(document.to_string(), "held!");
} }
#[test] #[test]
fn test_complex_line() { fn test_complex_line() {
let mut document = TextDocument::from_deltas(vec![]); let document = TextDocument::from_deltas(vec![]);
assert_eq!(document.is_ok(), true);
let mut document = document.unwrap();
document.update("hello"); document.update("hello").unwrap();
assert_eq!(document.to_string(), "hello"); assert_eq!(document.to_string(), "hello");
assert_eq!(document.get_deltas().len(), 1); assert_eq!(document.get_deltas().len(), 1);
assert_eq!(document.get_deltas()[0].operations.len(), 1); assert_eq!(document.get_deltas()[0].operations.len(), 1);
@ -68,7 +78,7 @@ fn test_complex_line() {
Operation::Insert((0, "hello".to_string())) Operation::Insert((0, "hello".to_string()))
); );
document.update("hello world"); document.update("hello world").unwrap();
assert_eq!(document.to_string(), "hello world"); assert_eq!(document.to_string(), "hello world");
assert_eq!(document.get_deltas().len(), 2); assert_eq!(document.get_deltas().len(), 2);
assert_eq!(document.get_deltas()[1].operations.len(), 1); assert_eq!(document.get_deltas()[1].operations.len(), 1);
@ -77,7 +87,7 @@ fn test_complex_line() {
Operation::Insert((5, " world".to_string())) Operation::Insert((5, " world".to_string()))
); );
document.update("held!"); document.update("held!").unwrap();
assert_eq!(document.to_string(), "held!"); assert_eq!(document.to_string(), "held!");
assert_eq!(document.get_deltas().len(), 3); assert_eq!(document.get_deltas().len(), 3);
assert_eq!(document.get_deltas()[2].operations.len(), 2); assert_eq!(document.get_deltas()[2].operations.len(), 2);
@ -93,9 +103,11 @@ fn test_complex_line() {
#[test] #[test]
fn test_multiline_add() { fn test_multiline_add() {
let mut document = TextDocument::from_deltas(vec![]); let document = TextDocument::from_deltas(vec![]);
assert_eq!(document.is_ok(), true);
let mut document = document.unwrap();
document.update("first"); document.update("first").unwrap();
assert_eq!(document.to_string(), "first"); assert_eq!(document.to_string(), "first");
assert_eq!(document.get_deltas().len(), 1); assert_eq!(document.get_deltas().len(), 1);
assert_eq!(document.get_deltas()[0].operations.len(), 1); assert_eq!(document.get_deltas()[0].operations.len(), 1);
@ -104,7 +116,7 @@ fn test_multiline_add() {
Operation::Insert((0, "first".to_string())) Operation::Insert((0, "first".to_string()))
); );
document.update("first\ntwo"); document.update("first\ntwo").unwrap();
assert_eq!(document.to_string(), "first\ntwo"); assert_eq!(document.to_string(), "first\ntwo");
assert_eq!(document.get_deltas().len(), 2); assert_eq!(document.get_deltas().len(), 2);
assert_eq!(document.get_deltas()[1].operations.len(), 1); assert_eq!(document.get_deltas()[1].operations.len(), 1);
@ -113,7 +125,7 @@ fn test_multiline_add() {
Operation::Insert((5, "\ntwo".to_string())) Operation::Insert((5, "\ntwo".to_string()))
); );
document.update("first line\nline two"); document.update("first line\nline two").unwrap();
assert_eq!(document.to_string(), "first line\nline two"); assert_eq!(document.to_string(), "first line\nline two");
assert_eq!(document.get_deltas().len(), 3); assert_eq!(document.get_deltas().len(), 3);
assert_eq!(document.get_deltas()[2].operations.len(), 2); assert_eq!(document.get_deltas()[2].operations.len(), 2);
@ -129,9 +141,11 @@ fn test_multiline_add() {
#[test] #[test]
fn test_multiline_remove() { fn test_multiline_remove() {
let mut document = TextDocument::from_deltas(vec![]); let document = TextDocument::from_deltas(vec![]);
assert_eq!(document.is_ok(), true);
let mut document = document.unwrap();
document.update("first line\nline two"); document.update("first line\nline two").unwrap();
assert_eq!(document.to_string(), "first line\nline two"); assert_eq!(document.to_string(), "first line\nline two");
assert_eq!(document.get_deltas().len(), 1); assert_eq!(document.get_deltas().len(), 1);
assert_eq!(document.get_deltas()[0].operations.len(), 1); assert_eq!(document.get_deltas()[0].operations.len(), 1);
@ -140,7 +154,7 @@ fn test_multiline_remove() {
Operation::Insert((0, "first line\nline two".to_string())) Operation::Insert((0, "first line\nline two".to_string()))
); );
document.update("first\ntwo"); document.update("first\ntwo").unwrap();
assert_eq!(document.to_string(), "first\ntwo"); assert_eq!(document.to_string(), "first\ntwo");
assert_eq!(document.get_deltas().len(), 2); assert_eq!(document.get_deltas().len(), 2);
assert_eq!(document.get_deltas()[1].operations.len(), 2); assert_eq!(document.get_deltas()[1].operations.len(), 2);
@ -153,7 +167,7 @@ fn test_multiline_remove() {
Operation::Delete((6, 5)) Operation::Delete((6, 5))
); );
document.update("first"); document.update("first").unwrap();
assert_eq!(document.to_string(), "first"); assert_eq!(document.to_string(), "first");
assert_eq!(document.get_deltas().len(), 3); assert_eq!(document.get_deltas().len(), 3);
assert_eq!(document.get_deltas()[2].operations.len(), 1); assert_eq!(document.get_deltas()[2].operations.len(), 1);
@ -162,7 +176,7 @@ fn test_multiline_remove() {
Operation::Delete((5, 4)) Operation::Delete((5, 4))
); );
document.update(""); document.update("").unwrap();
assert_eq!(document.to_string(), ""); assert_eq!(document.to_string(), "");
assert_eq!(document.get_deltas().len(), 4); assert_eq!(document.get_deltas().len(), 4);
assert_eq!(document.get_deltas()[3].operations.len(), 1); assert_eq!(document.get_deltas()[3].operations.len(), 1);