operational_transform/
lib.rs

1//! A library for Operational Transformation
2//!
3//! Operational transformation (OT) is a technology for supporting a range of
4//! collaboration functionalities in advanced collaborative software systems.
5//! [[1]](https://en.wikipedia.org/wiki/Operational_transformation)
6//!
7//! When working on the same document over the internet concurrent operations
8//! from multiple users might be in conflict. **Operational Transform** can help
9//! to resolve conflicts in such a way that documents stay in sync.
10//!
11//! The basic operations that are supported are:
12//! - Retain(n): Move the cursor `n` positions forward
13//! - Delete(n): Delete `n` characters at the current position
14//! - Insert(s): Insert the string `s` at the current position
15//!
16//! This library can be  used to...
17//!
18//! ... compose sequences of operations:
19//! ```rust
20//! use operational_transform::OperationSeq;
21//!
22//! let mut a = OperationSeq::default();
23//! a.insert("abc");
24//! let mut b = OperationSeq::default();
25//! b.retain(3);
26//! b.insert("def");
27//! let after_a = a.apply("").unwrap();
28//! let after_b = b.apply(&after_a).unwrap();
29//! let c = a.compose(&b).unwrap();
30//! let after_ab = a.compose(&b).unwrap().apply("").unwrap();
31//! assert_eq!(after_ab, after_b);
32//! ```
33//!
34//! ... transform sequences of operations
35//! ```rust
36//! use operational_transform::OperationSeq;
37//!
38//! let s = "abc";
39//! let mut a = OperationSeq::default();
40//! a.retain(3);
41//! a.insert("def");
42//! let mut b = OperationSeq::default();
43//! b.retain(3);
44//! b.insert("ghi");
45//! let (a_prime, b_prime) = a.transform(&b).unwrap();
46//! let ab_prime = a.compose(&b_prime).unwrap();
47//! let ba_prime = b.compose(&a_prime).unwrap();
48//! let after_ab_prime = ab_prime.apply(s).unwrap();
49//! let after_ba_prime = ba_prime.apply(s).unwrap();
50//! assert_eq!(ab_prime, ba_prime);
51//! assert_eq!(after_ab_prime, after_ba_prime);
52//! ```
53//!
54//! ... invert sequences of operations
55//! ```rust
56//! use operational_transform::OperationSeq;
57//!
58//! let s = "abc";
59//! let mut o = OperationSeq::default();
60//! o.retain(3);
61//! o.insert("def");
62//! let p = o.invert(s);
63//! assert_eq!(p.apply(&o.apply(s).unwrap()).unwrap(), s);
64//! ```
65//!
66//! ## Features
67//!
68//! Serialization is supported by using the `serde` feature.
69//!
70//! - Delete(n) will be serialized to -n
71//! - Insert(s) will be serialized to "{s}"
72//! - Retain(n) will be serialized to n
73//!
74//! ```rust,ignore
75//! use operational_transform::OperationSeq;
76//! use serde_json;
77//!
78//! let o: OperationSeq = serde_json::from_str("[1,-1,\"abc\"]").unwrap();
79//! let mut o_exp = OperationSeq::default();
80//! o_exp.retain(1);
81//! o_exp.delete(1);
82//! o_exp.insert("abc");
83//! assert_eq!(o, o_exp);
84//! ```
85//!
86//! ## Acknowledgement
87//! In the current state the code is ported from
88//! [here](https://github.com/Operational-Transformation/ot.js/). It might
89//! change in the future as there is much room for optimisation and also
90//! usability.
91
92#[cfg(feature = "serde")]
93pub mod serde;
94
95#[cfg(any(test, bench))]
96pub mod utilities;
97
98use bytecount::num_chars;
99use std::{cmp::Ordering, error::Error, fmt, iter::FromIterator};
100
101/// A single operation to be executed at the cursor's current position.
102#[derive(Debug, Clone, PartialEq)]
103pub enum Operation {
104    // Deletes n characters at the current cursor position.
105    Delete(u64),
106    // Moves the cursor n positions forward.
107    Retain(u64),
108    // Inserts string at the current cursor position.
109    Insert(String),
110}
111
112/// A sequence of `Operation`s on text.
113#[derive(Clone, Debug, PartialEq, Default)]
114pub struct OperationSeq {
115    // The consecutive operations to be applied to the target.
116    ops: Vec<Operation>,
117    // The required length of a string these operations can be applied to.
118    base_len: usize,
119    // The length of the resulting string after the operations have been
120    // applied.
121    target_len: usize,
122}
123
124impl FromIterator<Operation> for OperationSeq {
125    fn from_iter<T: IntoIterator<Item = Operation>>(ops: T) -> Self {
126        let mut operations = OperationSeq::default();
127        for op in ops {
128            operations.add(op);
129        }
130        operations
131    }
132}
133
134/// Error for failed operational transform operations.
135#[derive(Clone, Debug)]
136pub struct OTError;
137
138impl fmt::Display for OTError {
139    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140        write!(f, "incompatible lengths")
141    }
142}
143
144impl Error for OTError {
145    fn source(&self) -> Option<&(dyn Error + 'static)> {
146        None
147    }
148}
149
150impl OperationSeq {
151    /// Creates a store for operatations which does not need to allocate  until
152    /// `capacity` operations have been stored inside.
153    #[inline]
154    pub fn with_capacity(capacity: usize) -> Self {
155        Self {
156            ops: Vec::with_capacity(capacity),
157            base_len: 0,
158            target_len: 0,
159        }
160    }
161
162    /// Merges the operation with `other` into one operation while preserving
163    /// the changes of both. Or, in other words, for each input string S and a
164    /// pair of consecutive operations A and B.
165    ///     `apply(apply(S, A), B) = apply(S, compose(A, B))`
166    /// must hold.
167    ///
168    /// # Error
169    ///
170    /// Returns an `OTError` if the operations are not composable due to length
171    /// conflicts.
172    pub fn compose(&self, other: &Self) -> Result<Self, OTError> {
173        if self.target_len != other.base_len {
174            return Err(OTError);
175        }
176
177        let mut new_op_seq = OperationSeq::default();
178        let mut ops1 = self.ops.iter().cloned();
179        let mut ops2 = other.ops.iter().cloned();
180
181        let mut maybe_op1 = ops1.next();
182        let mut maybe_op2 = ops2.next();
183        loop {
184            match (&maybe_op1, &maybe_op2) {
185                (None, None) => break,
186                (Some(Operation::Delete(i)), _) => {
187                    new_op_seq.delete(*i);
188                    maybe_op1 = ops1.next();
189                }
190                (_, Some(Operation::Insert(s))) => {
191                    new_op_seq.insert(s);
192                    maybe_op2 = ops2.next();
193                }
194                (None, _) | (_, None) => {
195                    return Err(OTError);
196                }
197                (Some(Operation::Retain(i)), Some(Operation::Retain(j))) => match i.cmp(j) {
198                    Ordering::Less => {
199                        new_op_seq.retain(*i);
200                        maybe_op2 = Some(Operation::Retain(*j - *i));
201                        maybe_op1 = ops1.next();
202                    }
203                    std::cmp::Ordering::Equal => {
204                        new_op_seq.retain(*i);
205                        maybe_op1 = ops1.next();
206                        maybe_op2 = ops2.next();
207                    }
208                    std::cmp::Ordering::Greater => {
209                        new_op_seq.retain(*j);
210                        maybe_op1 = Some(Operation::Retain(*i - *j));
211                        maybe_op2 = ops2.next();
212                    }
213                },
214                (Some(Operation::Insert(s)), Some(Operation::Delete(j))) => {
215                    match (num_chars(s.as_bytes()) as u64).cmp(j) {
216                        Ordering::Less => {
217                            maybe_op2 =
218                                Some(Operation::Delete(*j - num_chars(s.as_bytes()) as u64));
219                            maybe_op1 = ops1.next();
220                        }
221                        Ordering::Equal => {
222                            maybe_op1 = ops1.next();
223                            maybe_op2 = ops2.next();
224                        }
225                        Ordering::Greater => {
226                            maybe_op1 =
227                                Some(Operation::Insert(s.chars().skip(*j as usize).collect()));
228                            maybe_op2 = ops2.next();
229                        }
230                    }
231                }
232                (Some(Operation::Insert(s)), Some(Operation::Retain(j))) => {
233                    match (num_chars(s.as_bytes()) as u64).cmp(j) {
234                        Ordering::Less => {
235                            new_op_seq.insert(s);
236                            maybe_op2 =
237                                Some(Operation::Retain(*j - num_chars(s.as_bytes()) as u64));
238                            maybe_op1 = ops1.next();
239                        }
240                        Ordering::Equal => {
241                            new_op_seq.insert(s);
242                            maybe_op1 = ops1.next();
243                            maybe_op2 = ops2.next();
244                        }
245                        Ordering::Greater => {
246                            let chars = &mut s.chars();
247                            new_op_seq.insert(&chars.take(*j as usize).collect::<String>());
248                            maybe_op1 = Some(Operation::Insert(chars.collect()));
249                            maybe_op2 = ops2.next();
250                        }
251                    }
252                }
253                (Some(Operation::Retain(i)), Some(Operation::Delete(j))) => match i.cmp(j) {
254                    Ordering::Less => {
255                        new_op_seq.delete(*i);
256                        maybe_op2 = Some(Operation::Delete(*j - *i));
257                        maybe_op1 = ops1.next();
258                    }
259                    Ordering::Equal => {
260                        new_op_seq.delete(*j);
261                        maybe_op2 = ops2.next();
262                        maybe_op1 = ops1.next();
263                    }
264                    Ordering::Greater => {
265                        new_op_seq.delete(*j);
266                        maybe_op1 = Some(Operation::Retain(*i - *j));
267                        maybe_op2 = ops2.next();
268                    }
269                },
270            };
271        }
272        Ok(new_op_seq)
273    }
274
275    fn add(&mut self, op: Operation) {
276        match op {
277            Operation::Delete(i) => self.delete(i),
278            Operation::Insert(s) => self.insert(&s),
279            Operation::Retain(i) => self.retain(i),
280        }
281    }
282
283    /// Deletes `n` characters at the current cursor position.
284    pub fn delete(&mut self, n: u64) {
285        if n == 0 {
286            return;
287        }
288        self.base_len += n as usize;
289        if let Some(Operation::Delete(n_last)) = self.ops.last_mut() {
290            *n_last += n;
291        } else {
292            self.ops.push(Operation::Delete(n));
293        }
294    }
295
296    /// Inserts a `s` at the current cursor position.
297    pub fn insert(&mut self, s: &str) {
298        if s.is_empty() {
299            return;
300        }
301        self.target_len += num_chars(s.as_bytes());
302        let new_last = match self.ops.as_mut_slice() {
303            [.., Operation::Insert(s_last)] => {
304                *s_last += s;
305                return;
306            }
307            [.., Operation::Insert(s_pre_last), Operation::Delete(_)] => {
308                *s_pre_last += s;
309                return;
310            }
311            [.., op_last @ Operation::Delete(_)] => {
312                let new_last = op_last.clone();
313                *op_last = Operation::Insert(s.to_owned());
314                new_last
315            }
316            _ => Operation::Insert(s.to_owned()),
317        };
318        self.ops.push(new_last);
319    }
320
321    /// Moves the cursor `n` characters forwards.
322    pub fn retain(&mut self, n: u64) {
323        if n == 0 {
324            return;
325        }
326        self.base_len += n as usize;
327        self.target_len += n as usize;
328        if let Some(Operation::Retain(i_last)) = self.ops.last_mut() {
329            *i_last += n;
330        } else {
331            self.ops.push(Operation::Retain(n));
332        }
333    }
334
335    /// Transforms two operations A and B that happened concurrently and produces
336    /// two operations A' and B' (in an array) such that
337    ///     `apply(apply(S, A), B') = apply(apply(S, B), A')`.
338    /// This function is the heart of OT.
339    ///
340    /// # Error
341    ///
342    /// Returns an `OTError` if the operations cannot be transformed due to
343    /// length conflicts.
344    pub fn transform(&self, other: &Self) -> Result<(Self, Self), OTError> {
345        if self.base_len != other.base_len {
346            return Err(OTError);
347        }
348
349        let mut a_prime = OperationSeq::default();
350        let mut b_prime = OperationSeq::default();
351
352        let mut ops1 = self.ops.iter().cloned();
353        let mut ops2 = other.ops.iter().cloned();
354
355        let mut maybe_op1 = ops1.next();
356        let mut maybe_op2 = ops2.next();
357        loop {
358            match (&maybe_op1, &maybe_op2) {
359                (None, None) => break,
360                (Some(Operation::Insert(s)), _) => {
361                    a_prime.insert(s);
362                    b_prime.retain(num_chars(s.as_bytes()) as _);
363                    maybe_op1 = ops1.next();
364                }
365                (_, Some(Operation::Insert(s))) => {
366                    a_prime.retain(num_chars(s.as_bytes()) as _);
367                    b_prime.insert(s);
368                    maybe_op2 = ops2.next();
369                }
370                (None, _) => {
371                    return Err(OTError);
372                }
373                (_, None) => {
374                    return Err(OTError);
375                }
376                (Some(Operation::Retain(i)), Some(Operation::Retain(j))) => {
377                    match i.cmp(j) {
378                        Ordering::Less => {
379                            a_prime.retain(*i);
380                            b_prime.retain(*i);
381                            maybe_op2 = Some(Operation::Retain(*j - *i));
382                            maybe_op1 = ops1.next();
383                        }
384                        Ordering::Equal => {
385                            a_prime.retain(*i);
386                            b_prime.retain(*i);
387                            maybe_op1 = ops1.next();
388                            maybe_op2 = ops2.next();
389                        }
390                        Ordering::Greater => {
391                            a_prime.retain(*j);
392                            b_prime.retain(*j);
393                            maybe_op1 = Some(Operation::Retain(*i - *j));
394                            maybe_op2 = ops2.next();
395                        }
396                    };
397                }
398                (Some(Operation::Delete(i)), Some(Operation::Delete(j))) => match i.cmp(j) {
399                    Ordering::Less => {
400                        maybe_op2 = Some(Operation::Delete(*j - *i));
401                        maybe_op1 = ops1.next();
402                    }
403                    Ordering::Equal => {
404                        maybe_op1 = ops1.next();
405                        maybe_op2 = ops2.next();
406                    }
407                    Ordering::Greater => {
408                        maybe_op1 = Some(Operation::Delete(*i - *j));
409                        maybe_op2 = ops2.next();
410                    }
411                },
412                (Some(Operation::Delete(i)), Some(Operation::Retain(j))) => {
413                    match i.cmp(j) {
414                        Ordering::Less => {
415                            a_prime.delete(*i);
416                            maybe_op2 = Some(Operation::Retain(*j - *i));
417                            maybe_op1 = ops1.next();
418                        }
419                        Ordering::Equal => {
420                            a_prime.delete(*i);
421                            maybe_op1 = ops1.next();
422                            maybe_op2 = ops2.next();
423                        }
424                        Ordering::Greater => {
425                            a_prime.delete(*j);
426                            maybe_op1 = Some(Operation::Delete(*i - *j));
427                            maybe_op2 = ops2.next();
428                        }
429                    };
430                }
431                (Some(Operation::Retain(i)), Some(Operation::Delete(j))) => {
432                    match i.cmp(j) {
433                        Ordering::Less => {
434                            b_prime.delete(*i);
435                            maybe_op2 = Some(Operation::Delete(*j - *i));
436                            maybe_op1 = ops1.next();
437                        }
438                        Ordering::Equal => {
439                            b_prime.delete(*i);
440                            maybe_op1 = ops1.next();
441                            maybe_op2 = ops2.next();
442                        }
443                        Ordering::Greater => {
444                            b_prime.delete(*j);
445                            maybe_op1 = Some(Operation::Retain(*i - *j));
446                            maybe_op2 = ops2.next();
447                        }
448                    };
449                }
450            }
451        }
452
453        Ok((a_prime, b_prime))
454    }
455
456    /// Applies an operation to a string, returning a new string.
457    ///
458    /// # Error
459    ///
460    /// Returns an error if the operation cannot be applied due to length
461    /// conflicts.
462    pub fn apply(&self, s: &str) -> Result<String, OTError> {
463        if num_chars(s.as_bytes()) != self.base_len {
464            return Err(OTError);
465        }
466        let mut new_s = String::new();
467        let chars = &mut s.chars();
468        for op in &self.ops {
469            match op {
470                Operation::Retain(retain) => {
471                    for c in chars.take(*retain as usize) {
472                        new_s.push(c);
473                    }
474                }
475                Operation::Delete(delete) => {
476                    for _ in 0..*delete {
477                        chars.next();
478                    }
479                }
480                Operation::Insert(insert) => {
481                    new_s += insert;
482                }
483            }
484        }
485        Ok(new_s)
486    }
487
488    /// Computes the inverse of an operation. The inverse of an operation is the
489    /// operation that reverts the effects of the operation, e.g. when you have
490    /// an operation 'insert("hello "); skip(6);' then the inverse is
491    /// 'delete("hello "); skip(6);'. The inverse should be used for
492    /// implementing undo.
493    pub fn invert(&self, s: &str) -> Self {
494        let mut inverse = OperationSeq::default();
495        let chars = &mut s.chars();
496        for op in &self.ops {
497            match op {
498                Operation::Retain(retain) => {
499                    inverse.retain(*retain);
500                    for _ in 0..*retain {
501                        chars.next();
502                    }
503                }
504                Operation::Insert(insert) => {
505                    inverse.delete(num_chars(insert.as_bytes()) as u64);
506                }
507                Operation::Delete(delete) => {
508                    inverse.insert(&chars.take(*delete as usize).collect::<String>());
509                }
510            }
511        }
512        inverse
513    }
514
515    /// Checks if this operation has no effect.
516    #[inline]
517    pub fn is_noop(&self) -> bool {
518        matches!(self.ops.as_slice(), [] | [Operation::Retain(_)])
519    }
520
521    /// Returns the length of a string these operations can be applied to
522    #[inline]
523    pub fn base_len(&self) -> usize {
524        self.base_len
525    }
526
527    /// Returns the length of the resulting string after the operations have
528    /// been applied.
529    #[inline]
530    pub fn target_len(&self) -> usize {
531        self.target_len
532    }
533
534    /// Returns the wrapped sequence of operations.
535    #[inline]
536    pub fn ops(&self) -> &[Operation] {
537        &self.ops
538    }
539}
540
541#[cfg(test)]
542mod tests {
543    use super::*;
544    use crate::utilities::Rng;
545
546    #[test]
547    fn lengths() {
548        let mut o = OperationSeq::default();
549        assert_eq!(o.base_len, 0);
550        assert_eq!(o.target_len, 0);
551        o.retain(5);
552        assert_eq!(o.base_len, 5);
553        assert_eq!(o.target_len, 5);
554        o.insert("abc");
555        assert_eq!(o.base_len, 5);
556        assert_eq!(o.target_len, 8);
557        o.retain(2);
558        assert_eq!(o.base_len, 7);
559        assert_eq!(o.target_len, 10);
560        o.delete(2);
561        assert_eq!(o.base_len, 9);
562        assert_eq!(o.target_len, 10);
563    }
564
565    #[test]
566    fn sequence() {
567        let mut o = OperationSeq::default();
568        o.retain(5);
569        o.retain(0);
570        o.insert("lorem");
571        o.insert("");
572        o.delete(3);
573        o.delete(0);
574        assert_eq!(o.ops.len(), 3);
575    }
576
577    #[test]
578    fn apply() {
579        for _ in 0..1000 {
580            let mut rng = Rng::default();
581            let s = rng.gen_string(50);
582            let o = rng.gen_operation_seq(&s);
583            assert_eq!(num_chars(s.as_bytes()), o.base_len);
584            assert_eq!(o.apply(&s).unwrap().chars().count(), o.target_len);
585        }
586    }
587
588    #[test]
589    fn invert() {
590        for _ in 0..1000 {
591            let mut rng = Rng::default();
592            let s = rng.gen_string(50);
593            let o = rng.gen_operation_seq(&s);
594            let p = o.invert(&s);
595            assert_eq!(o.base_len, p.target_len);
596            assert_eq!(o.target_len, p.base_len);
597            assert_eq!(p.apply(&o.apply(&s).unwrap()).unwrap(), s);
598        }
599    }
600
601    #[test]
602    fn empty_ops() {
603        let mut o = OperationSeq::default();
604        o.retain(0);
605        o.insert("");
606        o.delete(0);
607        assert_eq!(o.ops.len(), 0);
608    }
609
610    #[test]
611    fn eq() {
612        let mut o1 = OperationSeq::default();
613        o1.delete(1);
614        o1.insert("lo");
615        o1.retain(2);
616        o1.retain(3);
617        let mut o2 = OperationSeq::default();
618        o2.delete(1);
619        o2.insert("l");
620        o2.insert("o");
621        o2.retain(5);
622        assert_eq!(o1, o2);
623        o1.delete(1);
624        o2.retain(1);
625        assert_ne!(o1, o2);
626    }
627
628    #[test]
629    fn ops_merging() {
630        let mut o = OperationSeq::default();
631        assert_eq!(o.ops.len(), 0);
632        o.retain(2);
633        assert_eq!(o.ops.len(), 1);
634        assert_eq!(o.ops.last(), Some(&Operation::Retain(2)));
635        o.retain(3);
636        assert_eq!(o.ops.len(), 1);
637        assert_eq!(o.ops.last(), Some(&Operation::Retain(5)));
638        o.insert("abc");
639        assert_eq!(o.ops.len(), 2);
640        assert_eq!(o.ops.last(), Some(&Operation::Insert("abc".to_owned())));
641        o.insert("xyz");
642        assert_eq!(o.ops.len(), 2);
643        assert_eq!(o.ops.last(), Some(&Operation::Insert("abcxyz".to_owned())));
644        o.delete(1);
645        assert_eq!(o.ops.len(), 3);
646        assert_eq!(o.ops.last(), Some(&Operation::Delete(1)));
647        o.delete(1);
648        assert_eq!(o.ops.len(), 3);
649        assert_eq!(o.ops.last(), Some(&Operation::Delete(2)));
650    }
651
652    #[test]
653    fn is_noop() {
654        let mut o = OperationSeq::default();
655        assert!(o.is_noop());
656        o.retain(5);
657        assert!(o.is_noop());
658        o.retain(3);
659        assert!(o.is_noop());
660        o.insert("lorem");
661        assert!(!o.is_noop());
662    }
663
664    #[test]
665    fn compose() {
666        for _ in 0..1000 {
667            let mut rng = Rng::default();
668            let s = rng.gen_string(20);
669            let a = rng.gen_operation_seq(&s);
670            let after_a = a.apply(&s).unwrap();
671            assert_eq!(a.target_len, num_chars(after_a.as_bytes()));
672            let b = rng.gen_operation_seq(&after_a);
673            let after_b = b.apply(&after_a).unwrap();
674            assert_eq!(b.target_len, num_chars(after_b.as_bytes()));
675            let ab = a.compose(&b).unwrap();
676            assert_eq!(ab.target_len, b.target_len);
677            let after_ab = ab.apply(&s).unwrap();
678            assert_eq!(after_b, after_ab);
679        }
680    }
681
682    #[test]
683    fn transform() {
684        for _ in 0..1000 {
685            let mut rng = Rng::default();
686            let s = rng.gen_string(20);
687            let a = rng.gen_operation_seq(&s);
688            let b = rng.gen_operation_seq(&s);
689            let (a_prime, b_prime) = a.transform(&b).unwrap();
690            let ab_prime = a.compose(&b_prime).unwrap();
691            let ba_prime = b.compose(&a_prime).unwrap();
692            let after_ab_prime = ab_prime.apply(&s).unwrap();
693            let after_ba_prime = ba_prime.apply(&s).unwrap();
694            assert_eq!(ab_prime, ba_prime);
695            assert_eq!(after_ab_prime, after_ba_prime);
696        }
697    }
698
699    #[test]
700    #[cfg(feature = "serde")]
701    fn serde() {
702        use serde_json;
703
704        let mut rng = Rng::default();
705
706        let o: OperationSeq = serde_json::from_str("[1,-1,\"abc\"]").unwrap();
707        let mut o_exp = OperationSeq::default();
708        o_exp.retain(1);
709        o_exp.delete(1);
710        o_exp.insert("abc");
711        assert_eq!(o, o_exp);
712        for _ in 0..1000 {
713            let s = rng.gen_string(20);
714            let o = rng.gen_operation_seq(&s);
715            assert_eq!(
716                o,
717                serde_json::from_str(&serde_json::to_string(&o).unwrap()).unwrap()
718            );
719        }
720    }
721}