[
  {
    "path": ".cargo/config",
    "content": "[alias]\nxtask = \"run --package xtask --\"\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\non:\n  pull_request:\n  push:\n    branches:\n      - master\n  # you can enable a schedule to build\n  # schedule:\n  # - cron: '00 01 * * *'\n\njobs:\n  check:\n    name: Check\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v2\n\n      - name: Install stable toolchain\n        uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          toolchain: stable\n          override: true\n\n      - uses: Swatinem/rust-cache@v1\n\n      - name: Run cargo check\n        uses: actions-rs/cargo@v1\n        with:\n          command: check\n\n  test:\n    name: Test Suite\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macos-latest, windows-latest]\n        rust: [stable]\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v2\n\n      - name: Install stable toolchain\n        uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          toolchain: ${{ matrix.rust }}\n          override: true\n\n      - uses: Swatinem/rust-cache@v1\n\n      - name: Run cargo test\n        uses: actions-rs/cargo@v1\n        with:\n          command: test\n\n  coverage:\n    name: Coverage\n    strategy:\n      matrix:\n        os: [ubuntu-latest]\n        rust: [stable]\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v2\n\n      - name: Install stable toolchain\n        uses: actions-rs/toolchain@v1\n        with:\n          toolchain: ${{ matrix.rust }}\n          override: true\n          components: llvm-tools-preview\n\n      - uses: Swatinem/rust-cache@v1\n\n      - name: Download grcov\n        run: |\n          mkdir -p \"${HOME}/.local/bin\"\n          curl -sL https://github.com/mozilla/grcov/releases/download/v0.8.10/grcov-x86_64-unknown-linux-gnu.tar.bz2 | tar jxf - -C \"${HOME}/.local/bin\"\n          echo \"$HOME/.local/bin\" >> $GITHUB_PATH\n\n      - name: Run xtask coverage\n        uses: actions-rs/cargo@v1\n        with:\n          command: xtask\n          args: coverage\n\n      - name: Upload to codecov.io\n        uses: codecov/codecov-action@v3\n        with:\n          files: coverage/*.lcov\n\n  lints:\n    name: Lints\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v2\n        with:\n          submodules: true\n\n      - name: Install stable toolchain\n        uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          toolchain: stable\n          override: true\n          components: rustfmt, clippy\n\n      - uses: Swatinem/rust-cache@v1\n\n      - name: Run cargo fmt\n        uses: actions-rs/cargo@v1\n        with:\n          command: fmt\n          args: --all -- --check\n\n      - name: Run cargo clippy\n        uses: actions-rs/cargo@v1\n        with:\n          command: clippy\n          args: -- -D warnings\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n.idea\n*.log\ntmp/\n\ntarget/\ncoverage/\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nmembers = [\n    \"operational-transform\",\n    \"xtask\"\n]\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License Copyright (c) 2020 bold\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is furnished\nto do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice (including the next\nparagraph) shall be included in all copies or substantial portions of the\nSoftware.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\nOR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF\nOR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# operational-transform\n\n[![Crates.io][crates-badge]][crates-url]\n[![docs.rs docs][docs-badge]][docs-url]\n[![ci][ci-badge]][ci-url]\n[![codecov](https://codecov.io/gh/spebern/operational-transform-rs/branch/master/graph/badge.svg?token=JM8YS98GDV)](https://codecov.io/gh/spebern/operational-transform-rs)\n\n[crates-badge]: https://img.shields.io/crates/v/operational-transform.svg\n[crates-url]: https://crates.io/crates/operational-transform\n[docs-badge]: https://img.shields.io/badge/docs-latest-blue.svg\n[docs-url]: https://docs.rs/operational-transform\n[ci-badge]: https://github.com/spebern/operational-transform-rs/workflows/Rust/badge.svg\n[ci-url]: https://github.com/spebern/operational-transform-rs/actions\n\nA library for Operational Transformation\n\nOperational transformation (OT) is a technology for supporting a range of\ncollaboration functionalities in advanced collaborative software systems.\n[[1]](https://en.wikipedia.org/wiki/Operational_transformation)\n\nWhen working on the same document over the internet concurrent operations\nfrom multiple users might be in conflict. **Operational Transform** can help\nto resolve conflicts in such a way that documents stay in sync.\n\nThe basic operations that are supported are:\n\n- Retain(n): Move the cursor `n` positions forward\n- Delete(n): Delete `n` characters at the current position\n- Insert(s): Insert the string `s` at the current position\n\nThis library can be used to...\n\n... compose sequences of operations:\n\n```rust\nuse operational_transform::OperationSeq;\n\nlet mut a = OperationSeq::default();\na.insert(\"abc\");\nlet mut b = OperationSeq::default();\nb.retain(3);\nb.insert(\"def\");\nlet after_a = a.apply(\"\").unwrap();\nlet after_b = b.apply(&after_a).unwrap();\nlet c = a.compose(&b).unwrap();\nlet after_ab = a.compose(&b).unwrap().apply(\"\").unwrap();\nassert_eq!(after_ab, after_b);\n```\n\n... transform sequences of operations\n\n```rust\nuse operational_transform::OperationSeq;\n\nlet s = \"abc\";\nlet mut a = OperationSeq::default();\na.retain(3);\na.insert(\"def\");\nlet mut b = OperationSeq::default();\nb.retain(3);\nb.insert(\"ghi\");\nlet (a_prime, b_prime) = a.transform(&b).unwrap();\nlet ab_prime = a.compose(&b_prime).unwrap();\nlet ba_prime = b.compose(&a_prime).unwrap();\nlet after_ab_prime = ab_prime.apply(s).unwrap();\nlet after_ba_prime = ba_prime.apply(s).unwrap();\nassert_eq!(ab_prime, ba_prime);\nassert_eq!(after_ab_prime, after_ba_prime);\n```\n\n... invert sequences of operations\n\n```rust\nuse operational_transform::OperationSeq;\n\nlet s = \"abc\";\nlet mut o = OperationSeq::default();\no.retain(3);\no.insert(\"def\");\nlet p = o.invert(s);\nassert_eq!(p.apply(&o.apply(s).unwrap()).unwrap(), s);\n```\n\n### Features\n\nSerialisation is supporeted by using the `serde` feature.\n\n- Delete(n) will be serialized to -n\n- Insert(s) will be serialized to \"{s}\"\n- Retain(n) will be serailized to n\n\n```rust\nuse operational_transform::OperationSeq;\nuse serde_json;\n\nlet o: OperationSeq = serde_json::from_str(\"[1,-1,\\\"abc\\\"]\").unwrap();\nlet mut o_exp = OperationSeq::default();\no_exp.retain(1);\no_exp.delete(1);\no_exp.insert(\"abc\");\nassert_eq!(o, o_exp);\n```\n\n### Acknowledgement\n\nIn the current state the code is ported from\n[here](https://github.com/Operational-Transformation/ot.js/). It might\nchange in the future as there is much room for optimisation and also\nusability.\n"
  },
  {
    "path": "operational-transform/.gitignore",
    "content": "/target\nCargo.lock\n"
  },
  {
    "path": "operational-transform/Cargo.toml",
    "content": "[package]\nname = \"operational-transform\"\nversion = \"0.6.1\"\nauthors = [\"bold <bold@cryptoguru.com>\"]\nedition = \"2018\"\nlicense = \"MIT\"\ndescription = \"A library for Operational Transformation\"\nreadme = \"README.md\"\nkeywords = [\"operational\", \"transform\", \"collaborative\", \"editing\"]\nrepository = \"https://github.com/spebern/operational-transform-rs\"\n\n[features]\nruntime-dispatch-simd = [\"bytecount/runtime-dispatch-simd\"]\ngeneric-simd = [\"bytecount/generic-simd\"]\n\n[dependencies]\nserde = { version = \"1\", default-features = false, optional = true }\nbytecount = \"0.6.0\"\n\n[dev-dependencies]\nrand = \"0.7.3\"\nserde_json = \"1.0.50\"\ncriterion = \"0.3\"\n\n[[bench]]\nname = \"benchmark\"\nharness = false\n"
  },
  {
    "path": "operational-transform/benches/benchmark.rs",
    "content": "use operational_transform::OperationSeq;\n\n#[path = \"../src/utilities.rs\"]\nmod utilities;\n\nuse criterion::{black_box, criterion_group, criterion_main, Criterion};\nuse utilities::Rng;\n\npub fn compose(c: &mut Criterion) {\n    let mut rng = Rng::from_seed(Default::default());\n    let input = (0..1000)\n        .map(|_| {\n            let s = rng.gen_string(20);\n            let a = rng.gen_operation_seq(&s);\n            let after_a = a.apply(&s).unwrap();\n            let b = rng.gen_operation_seq(&after_a);\n            (a, b)\n        })\n        .collect::<Vec<_>>();\n    c.bench_function(\"compose\", |b| {\n        b.iter(|| {\n            for (a, b) in input.iter() {\n                let _ = a.compose(black_box(b));\n            }\n        })\n    });\n}\n\npub fn transform(c: &mut Criterion) {\n    let mut rng = Rng::from_seed(Default::default());\n    let input = (0..1000)\n        .map(|_| {\n            let s = rng.gen_string(20);\n            let a = rng.gen_operation_seq(&s);\n            let b = rng.gen_operation_seq(&s);\n            (a, b)\n        })\n        .collect::<Vec<_>>();\n    c.bench_function(\"transform\", |b| {\n        b.iter(|| {\n            for (a, b) in input.iter() {\n                let _ = a.transform(black_box(b));\n            }\n        })\n    });\n}\n\npub fn invert(c: &mut Criterion) {\n    let mut rng = Rng::from_seed(Default::default());\n    let input = (0..1000)\n        .map(|_| {\n            let s = rng.gen_string(50);\n            let o = rng.gen_operation_seq(&s);\n            (o, s)\n        })\n        .collect::<Vec<_>>();\n    c.bench_function(\"invert\", |b| {\n        b.iter(|| {\n            for (o, s) in input.iter() {\n                let _ = o.invert(black_box(&s));\n            }\n        })\n    });\n}\n\npub fn apply(c: &mut Criterion) {\n    let mut rng = Rng::from_seed(Default::default());\n    let input = (0..1000)\n        .map(|_| {\n            let s = rng.gen_string(50);\n            let o = rng.gen_operation_seq(&s);\n            (o, s)\n        })\n        .collect::<Vec<_>>();\n    c.bench_function(\"apply\", |b| {\n        b.iter(|| {\n            for (o, s) in input.iter() {\n                let _ = o.apply(black_box(&s));\n            }\n        })\n    });\n}\n\ncriterion_group!(benches, compose, transform, invert, apply);\ncriterion_main!(benches);\n"
  },
  {
    "path": "operational-transform/src/lib.rs",
    "content": "//! A library for Operational Transformation\n//!\n//! Operational transformation (OT) is a technology for supporting a range of\n//! collaboration functionalities in advanced collaborative software systems.\n//! [[1]](https://en.wikipedia.org/wiki/Operational_transformation)\n//!\n//! When working on the same document over the internet concurrent operations\n//! from multiple users might be in conflict. **Operational Transform** can help\n//! to resolve conflicts in such a way that documents stay in sync.\n//!\n//! The basic operations that are supported are:\n//! - Retain(n): Move the cursor `n` positions forward\n//! - Delete(n): Delete `n` characters at the current position\n//! - Insert(s): Insert the string `s` at the current position\n//!\n//! This library can be  used to...\n//!\n//! ... compose sequences of operations:\n//! ```rust\n//! use operational_transform::OperationSeq;\n//!\n//! let mut a = OperationSeq::default();\n//! a.insert(\"abc\");\n//! let mut b = OperationSeq::default();\n//! b.retain(3);\n//! b.insert(\"def\");\n//! let after_a = a.apply(\"\").unwrap();\n//! let after_b = b.apply(&after_a).unwrap();\n//! let c = a.compose(&b).unwrap();\n//! let after_ab = a.compose(&b).unwrap().apply(\"\").unwrap();\n//! assert_eq!(after_ab, after_b);\n//! ```\n//!\n//! ... transform sequences of operations\n//! ```rust\n//! use operational_transform::OperationSeq;\n//!\n//! let s = \"abc\";\n//! let mut a = OperationSeq::default();\n//! a.retain(3);\n//! a.insert(\"def\");\n//! let mut b = OperationSeq::default();\n//! b.retain(3);\n//! b.insert(\"ghi\");\n//! let (a_prime, b_prime) = a.transform(&b).unwrap();\n//! let ab_prime = a.compose(&b_prime).unwrap();\n//! let ba_prime = b.compose(&a_prime).unwrap();\n//! let after_ab_prime = ab_prime.apply(s).unwrap();\n//! let after_ba_prime = ba_prime.apply(s).unwrap();\n//! assert_eq!(ab_prime, ba_prime);\n//! assert_eq!(after_ab_prime, after_ba_prime);\n//! ```\n//!\n//! ... invert sequences of operations\n//! ```rust\n//! use operational_transform::OperationSeq;\n//!\n//! let s = \"abc\";\n//! let mut o = OperationSeq::default();\n//! o.retain(3);\n//! o.insert(\"def\");\n//! let p = o.invert(s);\n//! assert_eq!(p.apply(&o.apply(s).unwrap()).unwrap(), s);\n//! ```\n//!\n//! ## Features\n//!\n//! Serialization is supported by using the `serde` feature.\n//!\n//! - Delete(n) will be serialized to -n\n//! - Insert(s) will be serialized to \"{s}\"\n//! - Retain(n) will be serialized to n\n//!\n//! ```rust,ignore\n//! use operational_transform::OperationSeq;\n//! use serde_json;\n//!\n//! let o: OperationSeq = serde_json::from_str(\"[1,-1,\\\"abc\\\"]\").unwrap();\n//! let mut o_exp = OperationSeq::default();\n//! o_exp.retain(1);\n//! o_exp.delete(1);\n//! o_exp.insert(\"abc\");\n//! assert_eq!(o, o_exp);\n//! ```\n//!\n//! ## Acknowledgment\n//! In the current state the code is ported from\n//! [here](https://github.com/Operational-Transformation/ot.js/). It might\n//! change in the future as there is much room for optimization and also\n//! usability.\n\n#[cfg(feature = \"serde\")]\npub mod serde;\n\n#[cfg(any(test, bench))]\npub mod utilities;\n\nuse bytecount::num_chars;\nuse std::{cmp::Ordering, error::Error, fmt, iter::FromIterator};\n\n/// A single operation to be executed at the cursor's current position.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum Operation {\n    // Deletes n characters at the current cursor position.\n    Delete(u64),\n    // Moves the cursor n positions forward.\n    Retain(u64),\n    // Inserts string at the current cursor position.\n    Insert(String),\n}\n\n/// A sequence of `Operation`s on text.\n#[derive(Clone, Debug, PartialEq, Eq, Default)]\npub struct OperationSeq {\n    // The consecutive operations to be applied to the target.\n    ops: Vec<Operation>,\n    // The required length of a string these operations can be applied to.\n    base_len: usize,\n    // The length of the resulting string after the operations have been\n    // applied.\n    target_len: usize,\n}\n\nimpl FromIterator<Operation> for OperationSeq {\n    fn from_iter<T: IntoIterator<Item = Operation>>(ops: T) -> Self {\n        let mut operations = OperationSeq::default();\n        for op in ops {\n            operations.add(op);\n        }\n        operations\n    }\n}\n\n/// Error for failed operational transform operations.\n#[derive(Clone, Debug)]\npub struct OTError;\n\nimpl fmt::Display for OTError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"incompatible lengths\")\n    }\n}\n\nimpl Error for OTError {\n    fn source(&self) -> Option<&(dyn Error + 'static)> {\n        None\n    }\n}\n\nimpl OperationSeq {\n    /// Creates a store for operations which does not need to allocate  until\n    /// `capacity` operations have been stored inside.\n    #[inline]\n    pub fn with_capacity(capacity: usize) -> Self {\n        Self {\n            ops: Vec::with_capacity(capacity),\n            base_len: 0,\n            target_len: 0,\n        }\n    }\n\n    /// Merges the operation with `other` into one operation while preserving\n    /// the changes of both. Or, in other words, for each input string S and a\n    /// pair of consecutive operations A and B.\n    ///     `apply(apply(S, A), B) = apply(S, compose(A, B))`\n    /// must hold.\n    ///\n    /// # Error\n    ///\n    /// Returns an `OTError` if the operations are not composable due to length\n    /// conflicts.\n    pub fn compose(&self, other: &Self) -> Result<Self, OTError> {\n        if self.target_len != other.base_len {\n            return Err(OTError);\n        }\n\n        let mut new_op_seq = OperationSeq::default();\n        let mut ops1 = self.ops.iter().cloned();\n        let mut ops2 = other.ops.iter().cloned();\n\n        let mut maybe_op1 = ops1.next();\n        let mut maybe_op2 = ops2.next();\n        loop {\n            match (&maybe_op1, &maybe_op2) {\n                (None, None) => break,\n                (Some(Operation::Delete(i)), _) => {\n                    new_op_seq.delete(*i);\n                    maybe_op1 = ops1.next();\n                }\n                (_, Some(Operation::Insert(s))) => {\n                    new_op_seq.insert(s);\n                    maybe_op2 = ops2.next();\n                }\n                (None, _) | (_, None) => {\n                    return Err(OTError);\n                }\n                (Some(Operation::Retain(i)), Some(Operation::Retain(j))) => match i.cmp(j) {\n                    Ordering::Less => {\n                        new_op_seq.retain(*i);\n                        maybe_op2 = Some(Operation::Retain(*j - *i));\n                        maybe_op1 = ops1.next();\n                    }\n                    std::cmp::Ordering::Equal => {\n                        new_op_seq.retain(*i);\n                        maybe_op1 = ops1.next();\n                        maybe_op2 = ops2.next();\n                    }\n                    std::cmp::Ordering::Greater => {\n                        new_op_seq.retain(*j);\n                        maybe_op1 = Some(Operation::Retain(*i - *j));\n                        maybe_op2 = ops2.next();\n                    }\n                },\n                (Some(Operation::Insert(s)), Some(Operation::Delete(j))) => {\n                    match (num_chars(s.as_bytes()) as u64).cmp(j) {\n                        Ordering::Less => {\n                            maybe_op2 =\n                                Some(Operation::Delete(*j - num_chars(s.as_bytes()) as u64));\n                            maybe_op1 = ops1.next();\n                        }\n                        Ordering::Equal => {\n                            maybe_op1 = ops1.next();\n                            maybe_op2 = ops2.next();\n                        }\n                        Ordering::Greater => {\n                            maybe_op1 =\n                                Some(Operation::Insert(s.chars().skip(*j as usize).collect()));\n                            maybe_op2 = ops2.next();\n                        }\n                    }\n                }\n                (Some(Operation::Insert(s)), Some(Operation::Retain(j))) => {\n                    match (num_chars(s.as_bytes()) as u64).cmp(j) {\n                        Ordering::Less => {\n                            new_op_seq.insert(s);\n                            maybe_op2 =\n                                Some(Operation::Retain(*j - num_chars(s.as_bytes()) as u64));\n                            maybe_op1 = ops1.next();\n                        }\n                        Ordering::Equal => {\n                            new_op_seq.insert(s);\n                            maybe_op1 = ops1.next();\n                            maybe_op2 = ops2.next();\n                        }\n                        Ordering::Greater => {\n                            let chars = &mut s.chars();\n                            new_op_seq.insert(&chars.take(*j as usize).collect::<String>());\n                            maybe_op1 = Some(Operation::Insert(chars.collect()));\n                            maybe_op2 = ops2.next();\n                        }\n                    }\n                }\n                (Some(Operation::Retain(i)), Some(Operation::Delete(j))) => match i.cmp(j) {\n                    Ordering::Less => {\n                        new_op_seq.delete(*i);\n                        maybe_op2 = Some(Operation::Delete(*j - *i));\n                        maybe_op1 = ops1.next();\n                    }\n                    Ordering::Equal => {\n                        new_op_seq.delete(*j);\n                        maybe_op2 = ops2.next();\n                        maybe_op1 = ops1.next();\n                    }\n                    Ordering::Greater => {\n                        new_op_seq.delete(*j);\n                        maybe_op1 = Some(Operation::Retain(*i - *j));\n                        maybe_op2 = ops2.next();\n                    }\n                },\n            };\n        }\n        Ok(new_op_seq)\n    }\n\n    fn add(&mut self, op: Operation) {\n        match op {\n            Operation::Delete(i) => self.delete(i),\n            Operation::Insert(s) => self.insert(&s),\n            Operation::Retain(i) => self.retain(i),\n        }\n    }\n\n    /// Deletes `n` characters at the current cursor position.\n    pub fn delete(&mut self, n: u64) {\n        if n == 0 {\n            return;\n        }\n        self.base_len += n as usize;\n        if let Some(Operation::Delete(n_last)) = self.ops.last_mut() {\n            *n_last += n;\n        } else {\n            self.ops.push(Operation::Delete(n));\n        }\n    }\n\n    /// Inserts a `s` at the current cursor position.\n    pub fn insert(&mut self, s: &str) {\n        if s.is_empty() {\n            return;\n        }\n        self.target_len += num_chars(s.as_bytes());\n        let new_last = match self.ops.as_mut_slice() {\n            [.., Operation::Insert(s_last)] => {\n                *s_last += s;\n                return;\n            }\n            [.., Operation::Insert(s_pre_last), Operation::Delete(_)] => {\n                *s_pre_last += s;\n                return;\n            }\n            [.., op_last @ Operation::Delete(_)] => {\n                let new_last = op_last.clone();\n                *op_last = Operation::Insert(s.to_owned());\n                new_last\n            }\n            _ => Operation::Insert(s.to_owned()),\n        };\n        self.ops.push(new_last);\n    }\n\n    /// Moves the cursor `n` characters forwards.\n    pub fn retain(&mut self, n: u64) {\n        if n == 0 {\n            return;\n        }\n        self.base_len += n as usize;\n        self.target_len += n as usize;\n        if let Some(Operation::Retain(i_last)) = self.ops.last_mut() {\n            *i_last += n;\n        } else {\n            self.ops.push(Operation::Retain(n));\n        }\n    }\n\n    /// Transforms two operations A and B that happened concurrently and produces\n    /// two operations A' and B' (in an array) such that\n    ///     `apply(apply(S, A), B') = apply(apply(S, B), A')`.\n    /// This function is the heart of OT.\n    ///\n    /// # Error\n    ///\n    /// Returns an `OTError` if the operations cannot be transformed due to\n    /// length conflicts.\n    pub fn transform(&self, other: &Self) -> Result<(Self, Self), OTError> {\n        if self.base_len != other.base_len {\n            return Err(OTError);\n        }\n\n        let mut a_prime = OperationSeq::default();\n        let mut b_prime = OperationSeq::default();\n\n        let mut ops1 = self.ops.iter().cloned();\n        let mut ops2 = other.ops.iter().cloned();\n\n        let mut maybe_op1 = ops1.next();\n        let mut maybe_op2 = ops2.next();\n        loop {\n            match (&maybe_op1, &maybe_op2) {\n                (None, None) => break,\n                (Some(Operation::Insert(s)), Some(Operation::Insert(t))) => match s.cmp(t) {\n                    Ordering::Less => {\n                        a_prime.insert(s);\n                        b_prime.retain(num_chars(s.as_bytes()) as _);\n                        maybe_op1 = ops1.next();\n                    }\n                    Ordering::Equal => {\n                        a_prime.insert(s);\n                        a_prime.retain(num_chars(s.as_bytes()) as _);\n                        b_prime.insert(s);\n                        b_prime.retain(num_chars(s.as_bytes()) as _);\n                        maybe_op1 = ops1.next();\n                        maybe_op2 = ops2.next();\n                    }\n                    Ordering::Greater => {\n                        a_prime.retain(num_chars(t.as_bytes()) as _);\n                        b_prime.insert(t);\n                        maybe_op2 = ops2.next();\n                    }\n                },\n                (Some(Operation::Insert(s)), _) => {\n                    a_prime.insert(s);\n                    b_prime.retain(num_chars(s.as_bytes()) as _);\n                    maybe_op1 = ops1.next();\n                }\n                (_, Some(Operation::Insert(s))) => {\n                    a_prime.retain(num_chars(s.as_bytes()) as _);\n                    b_prime.insert(s);\n                    maybe_op2 = ops2.next();\n                }\n                (None, _) | (_, None) => {\n                    return Err(OTError);\n                }\n                (Some(Operation::Retain(i)), Some(Operation::Retain(j))) => {\n                    match i.cmp(j) {\n                        Ordering::Less => {\n                            a_prime.retain(*i);\n                            b_prime.retain(*i);\n                            maybe_op2 = Some(Operation::Retain(*j - *i));\n                            maybe_op1 = ops1.next();\n                        }\n                        Ordering::Equal => {\n                            a_prime.retain(*i);\n                            b_prime.retain(*i);\n                            maybe_op1 = ops1.next();\n                            maybe_op2 = ops2.next();\n                        }\n                        Ordering::Greater => {\n                            a_prime.retain(*j);\n                            b_prime.retain(*j);\n                            maybe_op1 = Some(Operation::Retain(*i - *j));\n                            maybe_op2 = ops2.next();\n                        }\n                    };\n                }\n                (Some(Operation::Delete(i)), Some(Operation::Delete(j))) => match i.cmp(j) {\n                    Ordering::Less => {\n                        maybe_op2 = Some(Operation::Delete(*j - *i));\n                        maybe_op1 = ops1.next();\n                    }\n                    Ordering::Equal => {\n                        maybe_op1 = ops1.next();\n                        maybe_op2 = ops2.next();\n                    }\n                    Ordering::Greater => {\n                        maybe_op1 = Some(Operation::Delete(*i - *j));\n                        maybe_op2 = ops2.next();\n                    }\n                },\n                (Some(Operation::Delete(i)), Some(Operation::Retain(j))) => {\n                    match i.cmp(j) {\n                        Ordering::Less => {\n                            a_prime.delete(*i);\n                            maybe_op2 = Some(Operation::Retain(*j - *i));\n                            maybe_op1 = ops1.next();\n                        }\n                        Ordering::Equal => {\n                            a_prime.delete(*i);\n                            maybe_op1 = ops1.next();\n                            maybe_op2 = ops2.next();\n                        }\n                        Ordering::Greater => {\n                            a_prime.delete(*j);\n                            maybe_op1 = Some(Operation::Delete(*i - *j));\n                            maybe_op2 = ops2.next();\n                        }\n                    };\n                }\n                (Some(Operation::Retain(i)), Some(Operation::Delete(j))) => {\n                    match i.cmp(j) {\n                        Ordering::Less => {\n                            b_prime.delete(*i);\n                            maybe_op2 = Some(Operation::Delete(*j - *i));\n                            maybe_op1 = ops1.next();\n                        }\n                        Ordering::Equal => {\n                            b_prime.delete(*i);\n                            maybe_op1 = ops1.next();\n                            maybe_op2 = ops2.next();\n                        }\n                        Ordering::Greater => {\n                            b_prime.delete(*j);\n                            maybe_op1 = Some(Operation::Retain(*i - *j));\n                            maybe_op2 = ops2.next();\n                        }\n                    };\n                }\n            }\n        }\n\n        Ok((a_prime, b_prime))\n    }\n\n    /// Applies an operation to a string, returning a new string.\n    ///\n    /// # Error\n    ///\n    /// Returns an error if the operation cannot be applied due to length\n    /// conflicts.\n    pub fn apply(&self, s: &str) -> Result<String, OTError> {\n        if num_chars(s.as_bytes()) != self.base_len {\n            return Err(OTError);\n        }\n        let mut new_s = String::new();\n        let chars = &mut s.chars();\n        for op in &self.ops {\n            match op {\n                Operation::Retain(retain) => {\n                    for c in chars.take(*retain as usize) {\n                        new_s.push(c);\n                    }\n                }\n                Operation::Delete(delete) => {\n                    for _ in 0..*delete {\n                        chars.next();\n                    }\n                }\n                Operation::Insert(insert) => {\n                    new_s += insert;\n                }\n            }\n        }\n        Ok(new_s)\n    }\n\n    /// Computes the inverse of an operation. The inverse of an operation is the\n    /// operation that reverts the effects of the operation, e.g. when you have\n    /// an operation 'insert(\"hello \"); skip(6);' then the inverse is\n    /// 'delete(\"hello \"); skip(6);'. The inverse should be used for\n    /// implementing undo.\n    pub fn invert(&self, s: &str) -> Self {\n        let mut inverse = OperationSeq::default();\n        let chars = &mut s.chars();\n        for op in &self.ops {\n            match op {\n                Operation::Retain(retain) => {\n                    inverse.retain(*retain);\n                    for _ in 0..*retain {\n                        chars.next();\n                    }\n                }\n                Operation::Insert(insert) => {\n                    inverse.delete(num_chars(insert.as_bytes()) as u64);\n                }\n                Operation::Delete(delete) => {\n                    inverse.insert(&chars.take(*delete as usize).collect::<String>());\n                }\n            }\n        }\n        inverse\n    }\n\n    /// Checks if this operation has no effect.\n    #[inline]\n    pub fn is_noop(&self) -> bool {\n        matches!(self.ops.as_slice(), [] | [Operation::Retain(_)])\n    }\n\n    /// Returns the length of a string these operations can be applied to\n    #[inline]\n    pub fn base_len(&self) -> usize {\n        self.base_len\n    }\n\n    /// Returns the length of the resulting string after the operations have\n    /// been applied.\n    #[inline]\n    pub fn target_len(&self) -> usize {\n        self.target_len\n    }\n\n    /// Returns the wrapped sequence of operations.\n    #[inline]\n    pub fn ops(&self) -> &[Operation] {\n        &self.ops\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::utilities::Rng;\n\n    #[test]\n    fn lengths() {\n        let mut o = OperationSeq::default();\n        assert_eq!(o.base_len, 0);\n        assert_eq!(o.target_len, 0);\n        o.retain(5);\n        assert_eq!(o.base_len, 5);\n        assert_eq!(o.target_len, 5);\n        o.insert(\"abc\");\n        assert_eq!(o.base_len, 5);\n        assert_eq!(o.target_len, 8);\n        o.retain(2);\n        assert_eq!(o.base_len, 7);\n        assert_eq!(o.target_len, 10);\n        o.delete(2);\n        assert_eq!(o.base_len, 9);\n        assert_eq!(o.target_len, 10);\n    }\n\n    #[test]\n    fn sequence() {\n        let mut o = OperationSeq::default();\n        o.retain(5);\n        o.retain(0);\n        o.insert(\"lorem\");\n        o.insert(\"\");\n        o.delete(3);\n        o.delete(0);\n        assert_eq!(o.ops.len(), 3);\n    }\n\n    #[test]\n    fn apply() {\n        for _ in 0..1000 {\n            let mut rng = Rng::default();\n            let s = rng.gen_string(50);\n            let o = rng.gen_operation_seq(&s);\n            assert_eq!(num_chars(s.as_bytes()), o.base_len);\n            assert_eq!(o.apply(&s).unwrap().chars().count(), o.target_len);\n        }\n    }\n\n    #[test]\n    fn invert() {\n        for _ in 0..1000 {\n            let mut rng = Rng::default();\n            let s = rng.gen_string(50);\n            let o = rng.gen_operation_seq(&s);\n            let p = o.invert(&s);\n            assert_eq!(o.base_len, p.target_len);\n            assert_eq!(o.target_len, p.base_len);\n            assert_eq!(p.apply(&o.apply(&s).unwrap()).unwrap(), s);\n        }\n    }\n\n    #[test]\n    fn empty_ops() {\n        let mut o = OperationSeq::default();\n        o.retain(0);\n        o.insert(\"\");\n        o.delete(0);\n        assert_eq!(o.ops.len(), 0);\n    }\n\n    #[test]\n    fn eq() {\n        let mut o1 = OperationSeq::default();\n        o1.delete(1);\n        o1.insert(\"lo\");\n        o1.retain(2);\n        o1.retain(3);\n        let mut o2 = OperationSeq::default();\n        o2.delete(1);\n        o2.insert(\"l\");\n        o2.insert(\"o\");\n        o2.retain(5);\n        assert_eq!(o1, o2);\n        o1.delete(1);\n        o2.retain(1);\n        assert_ne!(o1, o2);\n    }\n\n    #[test]\n    fn ops_merging() {\n        let mut o = OperationSeq::default();\n        assert_eq!(o.ops.len(), 0);\n        o.retain(2);\n        assert_eq!(o.ops.len(), 1);\n        assert_eq!(o.ops.last(), Some(&Operation::Retain(2)));\n        o.retain(3);\n        assert_eq!(o.ops.len(), 1);\n        assert_eq!(o.ops.last(), Some(&Operation::Retain(5)));\n        o.insert(\"abc\");\n        assert_eq!(o.ops.len(), 2);\n        assert_eq!(o.ops.last(), Some(&Operation::Insert(\"abc\".to_owned())));\n        o.insert(\"xyz\");\n        assert_eq!(o.ops.len(), 2);\n        assert_eq!(o.ops.last(), Some(&Operation::Insert(\"abcxyz\".to_owned())));\n        o.delete(1);\n        assert_eq!(o.ops.len(), 3);\n        assert_eq!(o.ops.last(), Some(&Operation::Delete(1)));\n        o.delete(1);\n        assert_eq!(o.ops.len(), 3);\n        assert_eq!(o.ops.last(), Some(&Operation::Delete(2)));\n    }\n\n    #[test]\n    fn is_noop() {\n        let mut o = OperationSeq::default();\n        assert!(o.is_noop());\n        o.retain(5);\n        assert!(o.is_noop());\n        o.retain(3);\n        assert!(o.is_noop());\n        o.insert(\"lorem\");\n        assert!(!o.is_noop());\n    }\n\n    #[test]\n    fn compose() {\n        for _ in 0..1000 {\n            let mut rng = Rng::default();\n            let s = rng.gen_string(20);\n            let a = rng.gen_operation_seq(&s);\n            let after_a = a.apply(&s).unwrap();\n            assert_eq!(a.target_len, num_chars(after_a.as_bytes()));\n            let b = rng.gen_operation_seq(&after_a);\n            let after_b = b.apply(&after_a).unwrap();\n            assert_eq!(b.target_len, num_chars(after_b.as_bytes()));\n            let ab = a.compose(&b).unwrap();\n            assert_eq!(ab.target_len, b.target_len);\n            let after_ab = ab.apply(&s).unwrap();\n            assert_eq!(after_b, after_ab);\n        }\n    }\n\n    #[test]\n    fn transform() {\n        for _ in 0..1000 {\n            let mut rng = Rng::default();\n            let s = rng.gen_string(20);\n            let a = rng.gen_operation_seq(&s);\n            let b = rng.gen_operation_seq(&s);\n            let (a_prime, b_prime) = a.transform(&b).unwrap();\n            let (b_prime_2, a_prime_2) = b.transform(&a).unwrap();\n            assert_eq!(a_prime, a_prime_2);\n            assert_eq!(b_prime, b_prime_2);\n            let ab_prime = a.compose(&b_prime).unwrap();\n            let ba_prime = b.compose(&a_prime).unwrap();\n            let after_ab_prime = ab_prime.apply(&s).unwrap();\n            let after_ba_prime = ba_prime.apply(&s).unwrap();\n            assert_eq!(ab_prime, ba_prime);\n            assert_eq!(after_ab_prime, after_ba_prime);\n        }\n    }\n\n    #[test]\n    #[cfg(feature = \"serde\")]\n    fn serde() {\n        use serde_json;\n\n        let mut rng = Rng::default();\n\n        let o: OperationSeq = serde_json::from_str(\"[1,-1,\\\"abc\\\"]\").unwrap();\n        let mut o_exp = OperationSeq::default();\n        o_exp.retain(1);\n        o_exp.delete(1);\n        o_exp.insert(\"abc\");\n        assert_eq!(o, o_exp);\n        for _ in 0..1000 {\n            let s = rng.gen_string(20);\n            let o = rng.gen_operation_seq(&s);\n            assert_eq!(\n                o,\n                serde_json::from_str(&serde_json::to_string(&o).unwrap()).unwrap()\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "operational-transform/src/serde.rs",
    "content": "use crate::{Operation, OperationSeq};\nuse serde::{\n    de::{self, Deserializer, SeqAccess, Visitor},\n    ser::{SerializeSeq, Serializer},\n    Deserialize, Serialize,\n};\nuse std::fmt;\n\nimpl Serialize for Operation {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: Serializer,\n    {\n        match self {\n            Operation::Retain(i) => serializer.serialize_u64(*i),\n            Operation::Delete(i) => serializer.serialize_i64(-(*i as i64)),\n            Operation::Insert(s) => serializer.serialize_str(s),\n        }\n    }\n}\n\nimpl<'de> Deserialize<'de> for Operation {\n    fn deserialize<D>(deserializer: D) -> Result<Operation, D::Error>\n    where\n        D: Deserializer<'de>,\n    {\n        struct OperationVisitor;\n\n        impl<'de> Visitor<'de> for OperationVisitor {\n            type Value = Operation;\n\n            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n                formatter.write_str(\"an integer between -2^64 and 2^63 or a string\")\n            }\n\n            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>\n            where\n                E: de::Error,\n            {\n                Ok(Operation::Retain(value as u64))\n            }\n\n            fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>\n            where\n                E: de::Error,\n            {\n                Ok(Operation::Delete((-value) as u64))\n            }\n\n            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>\n            where\n                E: de::Error,\n            {\n                Ok(Operation::Insert(value.to_owned()))\n            }\n        }\n\n        deserializer.deserialize_any(OperationVisitor)\n    }\n}\n\nimpl Serialize for OperationSeq {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: Serializer,\n    {\n        let mut seq = serializer.serialize_seq(Some(self.ops.len()))?;\n        for op in self.ops.iter() {\n            seq.serialize_element(op)?;\n        }\n        seq.end()\n    }\n}\n\nimpl<'de> Deserialize<'de> for OperationSeq {\n    fn deserialize<D>(deserializer: D) -> Result<OperationSeq, D::Error>\n    where\n        D: Deserializer<'de>,\n    {\n        struct OperationSeqVisitor;\n\n        impl<'de> Visitor<'de> for OperationSeqVisitor {\n            type Value = OperationSeq;\n\n            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n                formatter.write_str(\"a sequence\")\n            }\n\n            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>\n            where\n                A: SeqAccess<'de>,\n            {\n                let mut o = OperationSeq::default();\n                while let Some(op) = seq.next_element()? {\n                    o.add(op);\n                }\n                Ok(o)\n            }\n        }\n\n        deserializer.deserialize_seq(OperationSeqVisitor)\n    }\n}\n"
  },
  {
    "path": "operational-transform/src/utilities.rs",
    "content": "use crate::OperationSeq;\nuse rand::prelude::*;\nuse rand::Rng as WrappedRng;\n\npub struct Rng(StdRng);\n\nimpl Default for Rng {\n    fn default() -> Self {\n        Rng(StdRng::from_rng(thread_rng()).unwrap())\n    }\n}\n\nimpl Rng {\n    pub fn from_seed(seed: [u8; 32]) -> Self {\n        Rng(StdRng::from_seed(seed))\n    }\n\n    pub fn gen_string(&mut self, len: usize) -> String {\n        (0..len).map(|_| self.0.gen::<char>()).collect()\n    }\n\n    pub fn gen_operation_seq(&mut self, s: &str) -> OperationSeq {\n        let mut op = OperationSeq::default();\n        loop {\n            let left = s.chars().count() - op.base_len();\n            if left == 0 {\n                break;\n            }\n            let i = if left == 1 {\n                1\n            } else {\n                1 + self.0.gen_range(0, std::cmp::min(left - 1, 20))\n            };\n            match self.0.gen_range(0.0, 1.0) {\n                f if f < 0.2 => {\n                    op.insert(&self.gen_string(i));\n                }\n                f if f < 0.4 => {\n                    op.delete(i as u64);\n                }\n                _ => {\n                    op.retain(i as u64);\n                }\n            }\n        }\n        if self.0.gen_range(0.0, 1.0) < 0.3 {\n            op.insert(&(\"1\".to_owned() + &self.gen_string(10)));\n        }\n        op\n    }\n}\n"
  },
  {
    "path": "xtask/Cargo.toml",
    "content": "[package]\nname = \"xtask\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nxtaskops = \"^0.2.0\"\nanyhow = \"1\"\nclap = \"3\"\n"
  },
  {
    "path": "xtask/src/main.rs",
    "content": "#![allow(dead_code)]\n\nuse clap::{AppSettings, Arg, Command};\nuse xtaskops::ops;\nuse xtaskops::tasks;\n\nfn main() -> Result<(), anyhow::Error> {\n    let cli = Command::new(\"xtask\")\n        .setting(AppSettings::SubcommandRequiredElseHelp)\n        .subcommand(\n            Command::new(\"coverage\").arg(\n                Arg::new(\"dev\")\n                    .short('d')\n                    .long(\"dev\")\n                    .help(\"generate an html report\")\n                    .takes_value(false),\n            ),\n        )\n        .subcommand(Command::new(\"vars\"))\n        .subcommand(Command::new(\"ci\"))\n        .subcommand(Command::new(\"powerset\"))\n        .subcommand(Command::new(\"bloat-deps\"))\n        .subcommand(Command::new(\"bloat-time\"))\n        .subcommand(Command::new(\"docs\"));\n    let matches = cli.get_matches();\n\n    let root = ops::root_dir();\n    let res = match matches.subcommand() {\n        Some((\"coverage\", sm)) => tasks::coverage(sm.is_present(\"dev\")),\n        Some((\"vars\", _)) => {\n            println!(\"root: {:?}\", root);\n            Ok(())\n        }\n        Some((\"ci\", _)) => tasks::ci(),\n        Some((\"docs\", _)) => tasks::docs(),\n        Some((\"powerset\", _)) => tasks::powerset(),\n        Some((\"bloat-deps\", _)) => tasks::bloat_deps(),\n        Some((\"bloat-time\", _)) => tasks::bloat_time(),\n        _ => unreachable!(\"unreachable branch\"),\n    };\n    res\n}\n"
  }
]