From db0c696cf9306102c19aa2c5ed376797e5c08f0e Mon Sep 17 00:00:00 2001 From: alemi Date: Sun, 20 Aug 2023 08:24:58 +0200 Subject: [PATCH] fix: added factory doctests, caught bugs --- src/buffer/factory.rs | 98 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 6 deletions(-) diff --git a/src/buffer/factory.rs b/src/buffer/factory.rs index f979196..05cf36d 100644 --- a/src/buffer/factory.rs +++ b/src/buffer/factory.rs @@ -2,10 +2,73 @@ //! //! a helper trait to produce Operation Sequences, knowing the current //! state of the buffer +//! +//! ```rust +//! use codemp::buffer::factory::{OperationFactory, SimpleOperationFactory}; +//! +//! let mut factory = SimpleOperationFactory::from(""); +//! let op = factory.insert("asd", 0); +//! factory.update(op)?; +//! assert_eq!(factory.content(), "asd"); +//! # Ok::<(), codemp::ot::OTError>(()) +//! ``` +//! +//! use [OperationFactory::insert] to add new characters at a specific index +//! +//! ```rust +//! # use codemp::buffer::factory::{OperationFactory, SimpleOperationFactory}; +//! # let mut factory = SimpleOperationFactory::from("asd"); +//! factory.update(factory.insert(" dsa", 3))?; +//! assert_eq!(factory.content(), "asd dsa"); +//! # Ok::<(), codemp::ot::OTError>(()) +//! ``` +//! +//! use [OperationFactory::delta] to arbitrarily change text at any position +//! +//! ```rust +//! # use codemp::buffer::factory::{OperationFactory, SimpleOperationFactory}; +//! # let mut factory = SimpleOperationFactory::from("asd dsa"); +//! let op = factory.delta(2, " xxx ", 5).expect("replaced region is equal to origin"); +//! factory.update(op)?; +//! assert_eq!(factory.content(), "as xxx sa"); +//! # Ok::<(), codemp::ot::OTError>(()) +//! ``` +//! +//! use [OperationFactory::delete] to remove characters from given index +//! +//! ```rust +//! # use codemp::buffer::factory::{OperationFactory, SimpleOperationFactory}; +//! # let mut factory = SimpleOperationFactory::from("as xxx sa"); +//! factory.update(factory.delete(2, 5))?; +//! assert_eq!(factory.content(), "assa"); +//! # Ok::<(), codemp::ot::OTError>(()) +//! ``` +//! +//! use [OperationFactory::replace] to completely replace buffer content +//! +//! ```rust +//! # use codemp::buffer::factory::{OperationFactory, SimpleOperationFactory}; +//! # let mut factory = SimpleOperationFactory::from("assa"); +//! let op = factory.replace("from scratch").expect("replace is equal to origin"); +//! factory.update(op)?; +//! assert_eq!(factory.content(), "from scratch"); +//! # Ok::<(), codemp::ot::OTError>(()) +//! ``` +//! +//! use [OperationFactory::cancel] to remove characters at index, but backwards +//! +//! ```rust +//! # use codemp::buffer::factory::{OperationFactory, SimpleOperationFactory}; +//! # let mut factory = SimpleOperationFactory::from("from scratch"); +//! factory.update(factory.cancel(12, 8))?; +//! assert_eq!(factory.content(), "from"); +//! # Ok::<(), codemp::ot::OTError>(()) +//! ``` +//! use std::ops::Range; -use operational_transform::{OperationSeq, Operation}; +use operational_transform::{OperationSeq, Operation, OTError}; use similar::{TextDiff, ChangeTag}; /// calculate leading no-ops in given opseq @@ -44,7 +107,7 @@ pub trait OperationFactory { let mut out = OperationSeq::default(); let content = self.content(); let tail_skip = content.len() - end; // TODO len is number of bytes, not chars - let content_slice = &content[start..tail_skip]; + let content_slice = &content[start..end]; if content_slice == txt { // if slice equals given text, no operation should be taken @@ -82,9 +145,9 @@ pub trait OperationFactory { fn delete(&self, pos: u64, count: u64) -> OperationSeq { let mut out = OperationSeq::default(); let len = self.content().len() as u64; - out.retain(pos - count); + out.retain(pos); out.delete(count); - out.retain(len - pos); + out.retain(len - (pos+count)); out } @@ -92,9 +155,32 @@ pub trait OperationFactory { fn cancel(&self, pos: u64, count: u64) -> OperationSeq { let mut out = OperationSeq::default(); let len = self.content().len() as u64; - out.retain(pos); + out.retain(pos - count); out.delete(count); - out.retain(len - (pos+count)); + out.retain(len - pos); out } } + +/// a simple example operation factory that just wraps a string +pub struct SimpleOperationFactory(String); + +impl SimpleOperationFactory { + /// applies given OperationSeq to underlying string, changing it + pub fn update(&mut self, op: OperationSeq) -> Result<(), OTError> { + self.0 = op.apply(&self.0)?; + Ok(()) + } +} + +impl From::<&str> for SimpleOperationFactory { + fn from(value: &str) -> Self { + SimpleOperationFactory(value.to_string()) + } +} + +impl OperationFactory for SimpleOperationFactory { + fn content(&self) -> String { + self.0.clone() + } +}