|
@@ -1,6 +1,28 @@
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
/// The `Path` represents as a path to reference to the node in the `NodeTree`.
|
|
|
+/// ┌─────────┐
|
|
|
+/// │ Root │
|
|
|
+/// └─────────┼──────────┐
|
|
|
+/// │0: Node A │
|
|
|
+/// └──────────┼────────────┐
|
|
|
+/// │0: Node A-1 │
|
|
|
+/// ├────────────┤
|
|
|
+/// │1: Node A-2 │
|
|
|
+/// ┌──────────┼────────────┘
|
|
|
+/// │1: Node B │
|
|
|
+/// └──────────┼────────────┐
|
|
|
+/// │0: Node B-1 │
|
|
|
+/// ├────────────┤
|
|
|
+/// │1: Node B-2 │
|
|
|
+/// ┌──────────┼────────────┘
|
|
|
+/// │2: Node C │
|
|
|
+/// └──────────┘
|
|
|
+///
|
|
|
+/// The path of Node A will be [0]
|
|
|
+/// The path of Node A-1 will be [0,0]
|
|
|
+/// The path of Node A-2 will be [0,1]
|
|
|
+/// The path of Node B-2 will be [1,1]
|
|
|
#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Debug)]
|
|
|
pub struct Path(pub Vec<usize>);
|
|
|
|
|
@@ -49,21 +71,90 @@ impl From<&[usize]> for Path {
|
|
|
}
|
|
|
|
|
|
impl Path {
|
|
|
+ /// Calling this function if there are two changes want to modify the same path.
|
|
|
///
|
|
|
- ///
|
|
|
- /// The path will be changed is
|
|
|
/// # Arguments
|
|
|
///
|
|
|
- /// * `other`:
|
|
|
- /// * `offset`: represents the len of nodes referenced by this path
|
|
|
+ /// * `other`: the path that need to be transformed
|
|
|
+ /// * `offset`: represents the len of nodes referenced by the current path
|
|
|
+ ///
|
|
|
+ /// If two changes modify the same path or the path was shared by them. Then it needs to do the
|
|
|
+ /// transformation to make sure the changes are applied to the right path.
|
|
|
///
|
|
|
- /// returns: Path
|
|
|
+ /// returns: the path represents the position that the other path reference to.
|
|
|
///
|
|
|
/// # Examples
|
|
|
///
|
|
|
/// ```
|
|
|
+ /// use lib_ot::core::Path;
|
|
|
+ /// let path = Path(vec![0, 1]);
|
|
|
+ /// for (old_path, len_of_nodes, expected_path) in vec![
|
|
|
+ /// // Try to modify the path [0, 1], but someone has inserted one element before the
|
|
|
+ /// // current path [0,1] in advance. That causes the modified path [0,1] to no longer
|
|
|
+ /// // valid. It needs to do the transformation to get the right path.
|
|
|
+ /// //
|
|
|
+ /// // [0,2] is the path you want to modify.
|
|
|
+ /// (Path(vec![0, 1]), 1, Path(vec![0, 2])),
|
|
|
+ /// (Path(vec![0, 1]), 5, Path(vec![0, 6])),
|
|
|
+ /// (Path(vec![0, 2]), 1, Path(vec![0, 3])),
|
|
|
+ /// // Try to modify the path [0, 2,3,4], but someone has inserted one element before the
|
|
|
+ /// // current path [0,1] in advance. That cause the prefix path [0,2] of [0,2,3,4]
|
|
|
+ /// // no longer valid.
|
|
|
+ /// // It needs to do the transformation to get the right path. So [0,2] is transformed to [0,3]
|
|
|
+ /// // and the suffix [3,4] of the [0,2,3,4] remains the same. So the transformed result is
|
|
|
+ /// //
|
|
|
+ /// // [0,3,3,4]
|
|
|
+ /// (Path(vec![0, 2, 3, 4]), 1, Path(vec![0, 3, 3, 4])),
|
|
|
+ /// ] {
|
|
|
+ /// assert_eq!(path.transform(&old_path, len_of_nodes), expected_path);
|
|
|
+ /// }
|
|
|
+ /// // The path remains the same in the following test. Because the shared path is not changed.
|
|
|
+ /// let path = Path(vec![0, 1, 2]);
|
|
|
+ /// for (old_path, len_of_nodes, expected_path) in vec![
|
|
|
+ /// // Try to modify the path [0,0,0,1,2], but someone has inserted one element
|
|
|
+ /// // before [0,1,2]. [0,0,0,1,2] and [0,1,2] share the same path [0,x], because
|
|
|
+ /// // the element was inserted at [0,1,2] that didn't affect the shared path [0, x].
|
|
|
+ /// // So, after the transformation, the path is not changed.
|
|
|
+ /// (Path(vec![0, 0, 0, 1, 2]), 1, Path(vec![0, 0, 0, 1, 2])),
|
|
|
+ /// (Path(vec![0, 1]), 1, Path(vec![0, 1])),
|
|
|
+ /// ] {
|
|
|
+ /// assert_eq!(path.transform(&old_path, len_of_nodes), expected_path);
|
|
|
+ /// }
|
|
|
///
|
|
|
+ /// let path = Path(vec![1, 1]);
|
|
|
+ /// for (old_path, len_of_nodes, expected_path) in vec![(Path(vec![1, 0]), 1, Path(vec![1, 0]))] {
|
|
|
+ /// assert_eq!(path.transform(&old_path, len_of_nodes), expected_path);
|
|
|
+ /// }
|
|
|
/// ```
|
|
|
+ /// For example, client A and client B want to insert a node at the same index, the server applies
|
|
|
+ /// the changes made by client B. But, before applying the client A's changes, server transforms
|
|
|
+ /// the changes first in order to make sure that client A modify the right position. After that,
|
|
|
+ /// the changes can be applied to the server.
|
|
|
+ ///
|
|
|
+ /// ┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
|
+ /// │ Client A │ │ Server │ │ Client B │
|
|
|
+ /// └─────┬────┘ └─────┬────┘ └────┬─────┘
|
|
|
+ /// │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ┐ │
|
|
|
+ /// │ │ Root │
|
|
|
+ /// │ │ │ 0:A │ │
|
|
|
+ /// │ │ ─ ─ ─ ─ ─ ─ ─ ─ │
|
|
|
+ /// │ │ ◀───────────────────────│
|
|
|
+ /// │ │ Insert B at index 1 │
|
|
|
+ /// │ │ │
|
|
|
+ /// │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ┐ │
|
|
|
+ /// │ │ Root │
|
|
|
+ /// │ │ │ 0:A │ │
|
|
|
+ /// ├──────────────────────▶│ 1:B │
|
|
|
+ /// │ Insert C at index 1 │ └ ─ ─ ─ ─ ─ ─ ─ ┘ │
|
|
|
+ /// │ │ │
|
|
|
+ /// │ │ transform index 1 to 2 │
|
|
|
+ /// │ │ │
|
|
|
+ /// │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ │
|
|
|
+ /// │ │ Root │ │
|
|
|
+ /// │ │ │ 0:A │
|
|
|
+ /// ▼ ▼ 1:B │ ▼
|
|
|
+ /// │ 2:C
|
|
|
+ /// ─ ─ ─ ─ ─ ─ ─ ─ ┘
|
|
|
pub fn transform(&self, other: &Path, offset: usize) -> Path {
|
|
|
if self.len() > other.len() {
|
|
|
return other.clone();
|
|
@@ -81,7 +172,7 @@ impl Path {
|
|
|
let second_last_index = self.0.len() - 1;
|
|
|
let mut prefix: Vec<usize> = self.0[0..second_last_index].into();
|
|
|
let mut suffix: Vec<usize> = other.0[self.0.len()..].into();
|
|
|
- let last_value = self.0.last().unwrap().clone();
|
|
|
+ let last_value = *self.0.last().unwrap();
|
|
|
|
|
|
let other_second_last_value = other.0[second_last_index];
|
|
|
|
|
@@ -96,81 +187,4 @@ impl Path {
|
|
|
prefix.append(&mut suffix);
|
|
|
Path(prefix)
|
|
|
}
|
|
|
-
|
|
|
- // delta is default to be 1
|
|
|
- pub fn transform3(pre_insert_path: &Path, b: &Path, offset: i64) -> Path {
|
|
|
- if pre_insert_path.len() > b.len() {
|
|
|
- return b.clone();
|
|
|
- }
|
|
|
- if pre_insert_path.is_empty() || b.is_empty() {
|
|
|
- return b.clone();
|
|
|
- }
|
|
|
- // check the prefix
|
|
|
- for i in 0..(pre_insert_path.len() - 1) {
|
|
|
- if pre_insert_path.0[i] != b.0[i] {
|
|
|
- return b.clone();
|
|
|
- }
|
|
|
- }
|
|
|
- let mut prefix: Vec<usize> = pre_insert_path.0[0..(pre_insert_path.len() - 1)].into();
|
|
|
- let mut suffix: Vec<usize> = b.0[pre_insert_path.0.len()..].into();
|
|
|
- let prev_insert_last: usize = *pre_insert_path.0.last().unwrap();
|
|
|
- let b_at_index = b.0[pre_insert_path.0.len() - 1];
|
|
|
- if prev_insert_last <= b_at_index {
|
|
|
- prefix.push(((b_at_index as i64) + offset) as usize);
|
|
|
- } else {
|
|
|
- prefix.push(b_at_index);
|
|
|
- }
|
|
|
- prefix.append(&mut suffix);
|
|
|
-
|
|
|
- Path(prefix)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-#[cfg(test)]
|
|
|
-mod tests {
|
|
|
- use crate::core::Path;
|
|
|
- #[test]
|
|
|
- fn path_transform_test_1() {
|
|
|
- assert_eq!(
|
|
|
- { Path::transform3(&Path(vec![0, 1]), &Path(vec![0, 1]), 1) }.0,
|
|
|
- vec![0, 2]
|
|
|
- );
|
|
|
-
|
|
|
- assert_eq!(
|
|
|
- { Path::transform3(&Path(vec![0, 1]), &Path(vec![0, 1]), 5) }.0,
|
|
|
- vec![0, 6]
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn path_transform_test_2() {
|
|
|
- assert_eq!(
|
|
|
- { Path::transform3(&Path(vec![0, 1]), &Path(vec![0, 2]), 1) }.0,
|
|
|
- vec![0, 3]
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn path_transform_test_3() {
|
|
|
- assert_eq!(
|
|
|
- { Path::transform3(&Path(vec![0, 1]), &Path(vec![0, 2, 7, 8, 9]), 1) }.0,
|
|
|
- vec![0, 3, 7, 8, 9]
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn path_transform_no_changed_test() {
|
|
|
- assert_eq!(
|
|
|
- { Path::transform3(&Path(vec![0, 1, 2]), &Path(vec![0, 0, 7, 8, 9]), 1) }.0,
|
|
|
- vec![0, 0, 7, 8, 9]
|
|
|
- );
|
|
|
- assert_eq!(
|
|
|
- { Path::transform3(&Path(vec![0, 1, 2]), &Path(vec![0, 1]), 1) }.0,
|
|
|
- vec![0, 1]
|
|
|
- );
|
|
|
- assert_eq!(
|
|
|
- { Path::transform3(&Path(vec![1, 1]), &Path(vec![1, 0]), 1) }.0,
|
|
|
- vec![1, 0]
|
|
|
- );
|
|
|
- }
|
|
|
}
|