ot_rs/core/text.rs
1use super::error::OperationError;
2use super::operation::Operation;
3use std::str::Chars;
4
5/// `ops`
6/// 本质上是 `[op]` 类型, 定义了如何将一个字符串转换为另一个字符串的 `op` 序列。
7/// 注意:当 `baseLength == base.len()` 时,说明虚拟游标移动到该文本的尾部,由于 原子操作 `Operation` 定义的操作,
8/// 游标只能向后移动,所以此时,若需要进行 `Retain` 或者 `Delete`,则需要创建一个新的 `TextOperation`
9///
10/// # Example
11/// 针对字符串 `abc`,在 用户删除了 b,并在 c 会后插入了 d。
12/// ```
13/// use ot_rs::core::TextOperation;
14/// let mut ops = TextOperation::new();
15/// ops.retain(1).delete(1).retain(1).insert("d");
16/// let base = "abc";
17/// let after = "acd";
18/// assert_eq!(after, ops.apply(base).unwrap());
19/// ```
20#[derive(Debug)]
21pub struct TextOperation {
22 /// 原子操作
23 ops: Vec<Operation>,
24 /// Retain、 Delete 的长度
25 /// 在 apply(base) -> after 时,等于 len(base)
26 base_length: usize,
27 /// Retain、Insert 的长度
28 /// 在 apply(base) -> after 时,等于 len(after)
29 after_length: usize,
30}
31
32impl PartialEq for TextOperation {
33 /// # Example
34 /// ```
35 /// use ot_rs::core::TextOperation;
36 /// let mut ops1 = TextOperation::new();
37 /// let mut ops2 = TextOperation::new();
38 /// assert!(ops1 == ops2);
39 /// ops1.retain(1).delete(1).retain(1).insert("d");
40 /// assert!(ops1 != ops2);
41 /// ops2.retain(1).delete(1).retain(1).insert("d");
42 /// assert!(ops1 == ops2);
43 /// ops2.insert("1");
44 /// assert!(ops1 != ops2);
45 /// ```
46 fn eq(&self, other: &Self) -> bool {
47 if self.base_length != other.base_length {
48 return false;
49 }
50 if self.after_length != other.after_length {
51 return false;
52 }
53 if self.ops.len() != other.ops.len() {
54 return false;
55 }
56 return self
57 .ops
58 .iter()
59 .zip(&other.ops)
60 .filter(|&(a, b)| *a != *b)
61 .count()
62 == 0;
63 }
64}
65
66impl ToString for TextOperation {
67 /// # Example
68 /// ```
69 /// use ot_rs::core::TextOperation;
70 /// let mut ops = TextOperation::new();
71 /// assert_eq!("(0->0){}", ops.to_string());
72 /// ops.retain(1).delete(1).retain(1).insert("de\"");
73 /// assert_eq!(
74 /// "(3->5){retain(1).delete(1).retain(1).insert(\"de\\\"\")}",
75 /// ops.to_string()
76 /// );
77 /// ```
78 fn to_string(&self) -> String {
79 format!("({}->{}){{", self.base_length, self.after_length).to_string()
80 + &self
81 .ops
82 .iter()
83 .map(Operation::to_string)
84 .collect::<Vec<String>>()
85 .join(".")
86 + "}"
87 }
88}
89
90impl TextOperation {
91 // === 构造函数 ===
92 /// 构造函数,创建一个无操作的 TextOperation
93 /// # Example
94 /// ```
95 /// use ot_rs::core::TextOperation;
96 /// let mut ops = TextOperation::new();
97 /// assert_eq!("(0->0){}", ops.to_string());
98 /// ```
99 pub fn new() -> TextOperation {
100 return TextOperation {
101 ops: vec![],
102 base_length: 0,
103 after_length: 0,
104 };
105 }
106
107 // === 3 个 操作函数(接收 `&mut self`) ===
108
109 /// 跳过给定数量的字符
110 /// ```
111 /// use ot_rs::core::TextOperation;
112 /// let mut ops = TextOperation::new();
113 /// ops.retain(1);
114 /// assert_eq!("(1->1){retain(1)}", ops.to_string());
115 /// ops.retain(1);
116 /// assert_eq!("(2->2){retain(2)}", ops.to_string());
117 ///
118 pub fn retain(&mut self, n: usize) -> &mut TextOperation {
119 if n == 0 {
120 return self;
121 }
122 self.base_length += n;
123 self.after_length += n;
124
125 // R(x),R(y) -> R(x+y)
126 if let Some(Operation::Retain(last_n)) = self.ops.last_mut() {
127 *last_n += n;
128 } else {
129 self.ops.push(Operation::Retain(n))
130 }
131 return self;
132 }
133
134 /// 在当前位置插入一个字符串
135 /// # Example
136 /// ```
137 /// use ot_rs::core::TextOperation;
138 /// let mut ops = TextOperation::new();
139 /// ops.insert("a");
140 /// assert_eq!("(0->1){insert(\"a\")}", ops.to_string());
141 /// ops.insert("b");
142 /// // 两次连续的插入将合并
143 /// assert_eq!("(0->2){insert(\"ab\")}", ops.to_string());
144 /// ops.delete(1);
145 /// assert_eq!("(1->2){insert(\"ab\").delete(1)}", ops.to_string());
146 /// // I,D + I 将加入的 I 合并到前面的插入
147 /// ops.insert("c");
148 /// assert_eq!("(1->3){insert(\"abc\").delete(1)}", ops.to_string());
149 /// ops.retain(1).delete(1);
150 /// assert_eq!(
151 /// "(3->4){insert(\"abc\").delete(1).retain(1).delete(1)}",
152 /// ops.to_string()
153 /// );
154 /// // D + I 将变为 I,D
155 /// ops.insert("d");
156 /// assert_eq!(
157 /// "(3->5){insert(\"abc\").delete(1).retain(1).insert(\"d\").delete(1)}",
158 /// ops.to_string()
159 /// );
160 /// ```
161 pub fn insert<T: Into<String>>(&mut self, str: T) -> &mut TextOperation {
162 let str = str.into();
163 if str == "".to_string() {
164 return self;
165 }
166 self.after_length += str.chars().count();
167 match self.ops.split_last_mut() {
168 // 合并 I(x),I(y) -> I(x+y)
169 Some((Operation::Insert(last_str), _)) => last_str.push_str(str.as_str()),
170 Some((Operation::Delete(_), op_heads)) => {
171 // 始终保持 insert 在 delete 前面
172 match op_heads.last_mut() {
173 // 合并 I(s),D(x),I(y) -> I(s+y),D(x)
174 Some(Operation::Insert(last_str)) => last_str.push_str(str.as_str()),
175 // D(x),I(y) -> I(y),D(x)
176 // 参考实现没有 bug,第一步 `ops[ops.length] = ops[ops.length-1]` 相当于插入了一个元素 😂,本质上就是上面的说明
177 // https://github.com/Operational-Transformation/ot.js/blob/e9a3a0e214dd6c001e25515274bae0842a8415f2/lib/text-operation.js#L102
178 _ => {
179 let last_delete = self.ops.last().unwrap().clone();
180 *self.ops.last_mut().unwrap() = Operation::Insert(str);
181 self.ops.push(last_delete);
182 }
183 }
184 }
185 _ => self.ops.push(Operation::Insert(str)),
186 }
187 return self;
188 }
189
190 /// 删除当前位置的字符串
191 /// # Example
192 /// ```
193 /// use ot_rs::core::TextOperation;
194 /// let mut ops = TextOperation::new();
195 /// ops.delete(1);
196 /// assert_eq!("(1->0){delete(1)}", ops.to_string());
197 /// ops.delete(2);
198 /// assert_eq!("(3->0){delete(3)}", ops.to_string());
199 /// ```
200 pub fn delete(&mut self, n: usize) -> &mut TextOperation {
201 if n == 0 {
202 return self;
203 }
204 self.base_length += n;
205
206 // D(x),D(y) -> D(x+y)
207 if let Some(Operation::Delete(last_n)) = self.ops.last_mut() {
208 *last_n += n;
209 } else {
210 self.ops.push(Operation::Delete(n))
211 }
212 return self;
213 }
214
215 /// 测试该操作 apply 后是否不产生影响
216 /// # Example
217 /// ```
218 /// use ot_rs::core::TextOperation;
219 /// let mut ops = TextOperation::new();
220 /// assert!(ops.is_noop());
221 /// ops.retain(10);
222 /// assert!(ops.is_noop());
223 /// ```
224 pub fn is_noop(&self) -> bool {
225 match self.ops.len() {
226 0 => true,
227 1 => match self.ops.first() {
228 Some(&Operation::Retain(_)) => true,
229 _ => false,
230 },
231 _ => false,
232 }
233 }
234
235 /// 将 操作 apply 应用到 base 字符串中,并返回一个新字符串;
236 /// 如果输入的字符串和操作之间不匹配,抛出一个错误。
237 /// # Example
238 /// ```
239 /// use ot_rs::core::{OperationError, TextOperation};
240 /// // 正常情况
241 /// let mut ops = TextOperation::new();
242 /// ops.retain(1).delete(1).retain(1).insert("d");
243 /// let base = "abc";
244 /// let after = "acd";
245 /// assert_eq!(after, ops.apply(base).unwrap());
246 /// // 异常情况
247 /// let mut ops = TextOperation::new();
248 /// assert_eq!(
249 /// OperationError::OperationApplyStringNotCompatible,
250 /// ops.insert("a").apply("---").unwrap_err()
251 /// );
252 /// ```
253 pub fn apply<T: Into<String>>(&self, base: T) -> Result<String, OperationError> {
254 let base = base.into();
255 let base_len = base.chars().count();
256 if base_len != self.base_length {
257 return Err(OperationError::OperationApplyStringNotCompatible);
258 }
259
260 let base_chars = &mut base.chars(); // 这是一个迭代器,不能使用切片语法,因为字符串是 utf8
261 let mut buffer: Vec<String> = Vec::with_capacity(self.ops.len());
262 let mut cursor = 0usize;
263 for op in &self.ops {
264 match op {
265 &Operation::Retain(n) => {
266 if cursor + n > base_len {
267 return Err(OperationError::OperationMoreLeftString);
268 }
269 // 遍历迭代器返回 base 前 n 个字符
270 buffer.push(chars_take(base_chars, n));
271 cursor += n // 游标移动
272 }
273 Operation::Insert(v) => buffer.push(v.clone()),
274 &Operation::Delete(n) => {
275 if cursor + n > base_len {
276 return Err(OperationError::OperationMoreLeftString);
277 }
278 cursor += n;
279 // 遍历迭代器,skip 字符
280 chars_skip(base_chars, n);
281 }
282 }
283 }
284 // 不可能发生
285 // if cursor != base_len {
286 // return Err(OperationError::OperationNotCoverWholeString);
287 // }
288 return Ok(buffer.join(""));
289 }
290
291 /// 生成 该 Operation 的 逆操作,即求 ops' 且满足 `apply(apply(s, ops), ops') = s`。可以用来实现 undo
292 /// # Example
293 /// ```
294 /// use ot_rs::core::TextOperation;
295 /// let base = "abc";
296 ///
297 /// let mut ops = TextOperation::new();
298 /// ops.retain(1).delete(1).retain(1).insert("d");
299 /// assert_eq!(
300 /// base,
301 /// ops.invert(base)
302 /// .unwrap()
303 /// .apply(ops.apply(base).unwrap())
304 /// .unwrap()
305 /// );
306 /// ```
307 pub fn invert<T: Into<String>>(&self, base: T) -> Result<TextOperation, OperationError> {
308 let base = base.into();
309 let base_len = base.chars().count();
310 if base_len != self.base_length {
311 return Err(OperationError::OperationApplyStringNotCompatible);
312 }
313
314 let base_chars = &mut base.chars(); // 这是一个迭代器,不能使用切片语法,因为字符串是 utf8
315 let mut cursor = 0usize;
316 let mut inverse = TextOperation::new();
317 // abe
318 // R1, D1, Icd, D1,
319 // acd
320 // R1, Ib, D2, Ie
321 // abe
322 for op in &self.ops {
323 match op {
324 &Operation::Retain(n) => {
325 if cursor + n > base_len {
326 return Err(OperationError::OperationMoreLeftString);
327 }
328 inverse.retain(n);
329 cursor += n;
330 chars_skip(base_chars, n);
331 }
332 Operation::Insert(str) => {
333 inverse.delete(str.chars().count());
334 }
335 &Operation::Delete(n) => {
336 if cursor + n > base_len {
337 return Err(OperationError::OperationMoreLeftString);
338 }
339 inverse.insert(chars_take(base_chars, n));
340 cursor += n;
341 }
342 }
343 }
344 // 不可能发生
345 // if cursor != base_len {
346 // return Err(OperationError::OperationNotCoverWholeString);
347 // }
348 return Ok(inverse);
349 }
350
351 /// 合并连续的两个 文本操作,满足 `apply(apply(S, A), B) = apply(S, compose(A, B))`
352 /// # Example
353 /// ```
354 /// use ot_rs::core::TextOperation;
355 /// let base = "abc";
356 /// let mut ops1 = TextOperation::new();
357 /// ops1.retain(1).insert("123").delete(1).retain(1);
358 /// let after1 = ops1.apply(base).unwrap();
359 /// assert_eq!("a123c", after1);
360 ///
361 /// let mut ops2 = TextOperation::new();
362 /// ops2.retain(2)
363 /// .insert("$$$")
364 /// .delete(1)
365 /// .retain(1)
366 /// .insert("###")
367 /// .retain(1);
368 /// let after2 = ops2.apply(&after1).unwrap();
369 ///
370 /// assert_eq!("a1$$$3###c", after2);
371 /// let compose_ops = ops1.compose(&ops2).unwrap();
372 /// assert_eq!(after2, compose_ops.apply(base).unwrap());
373 /// ```
374 pub fn compose(&self, operation2: &TextOperation) -> Result<TextOperation, OperationError> {
375 if self.after_length != operation2.base_length {
376 return Err(OperationError::SecondBaseLengthNotEqualFirstAfterLength);
377 }
378
379 let mut ops1 = self.ops.split_first();
380 let mut ops2 = operation2.ops.split_first();
381 let mut tmp: Box<Operation>; // 修复 rust 生命周期检测
382
383 let mut composed = TextOperation::new();
384 // 思路大概是:
385 // 设置两个游标,同时遍历 ops1,ops2;
386 // 每一轮迭代,都相当于重新调用了 compose,是一个递归过程;
387 // 定义递归函数 compose(ops1, ops2, ops3) 将 ops1、ops2 合并成 ops3
388 // 因此我们只需按照递归的思路,思考初始的状态的9种组合即可
389 // 1. compose([R(x), ..ops1], [R(y), ..ops2], [])
390 // x == y: compose(ops1, ops2, [R(x)])
391 // x > y : compose([R(x-y), ..ops1], ops2, [R(y)])
392 // x < y : compose(ops1, [R(y-x), ..ops2], [R(y)])
393 // 2. compose([R(x), ..ops1], [I(y), ..ops2], [])
394 // : compose([R(x), ..ops1], ops2, [I(y)])
395 // 3. compose([R(x), ..ops1], [D(y), ..ops2], [])
396 // y < x : compose([R(x-y), ..ops1], ops2, [D(y)])
397 // y = x: compose(ops1, ops2, [D(y)])
398 // y > x : compose(ops1, [D(y-x) ,..ops2], [D(x)])
399 // 4. compose([I(x), ..ops1], [R(y), ..ops2], [])
400 // ...
401 // 在此就不全部枚举了,本质上就 op1、op2 将范围大的那个拆一部分出来,然后继续递归
402 // 将以上全部枚举出来后,进行剪枝,并转化为迭代的形式,就可以得到如下的算法
403 loop {
404 match (ops1, ops2) {
405 // None, None
406 (None, None) => break,
407 // D, _
408 (Some((&Operation::Delete(n1), ops_tail1)), _) => {
409 composed.delete(n1);
410 ops1 = ops_tail1.split_first();
411 continue;
412 }
413 // _, I
414 (_, Some((Operation::Insert(s), ops_tail))) => {
415 composed.insert(s.clone());
416 ops2 = ops_tail.split_first();
417 continue;
418 }
419 // None, _
420 (None, _) => return Err(OperationError::ComposeFirstTooShort),
421 // _, None
422 (_, None) => return Err(OperationError::ComposeFirstTooLong),
423 (
424 Some((&Operation::Retain(n1), ops_tail1)),
425 Some((&Operation::Retain(n2), ops_tail2)),
426 ) => {
427 if n1 > n2 {
428 composed.retain(n2);
429 tmp = Box::new(Operation::Retain(n1 - n2));
430 ops1 = Some((&tmp, ops_tail1));
431 ops2 = ops_tail2.split_first();
432 } else if n1 == n2 {
433 composed.retain(n1);
434 ops1 = ops_tail1.split_first();
435 ops2 = ops_tail2.split_first();
436 } else {
437 composed.retain(n1);
438 tmp = Box::new(Operation::Retain(n2 - n1));
439 ops2 = Some((&tmp, ops_tail2));
440 ops1 = ops_tail1.split_first();
441 }
442 }
443 // I, D
444 (
445 Some((Operation::Insert(s1), ops_tail1)),
446 Some((&Operation::Delete(n2), ops_tail2)),
447 ) => {
448 let l1 = s1.chars().count();
449 if l1 > n2 {
450 tmp = Box::new(Operation::Insert(chars_tail(&mut s1.chars(), n2)));
451 ops1 = Some((&tmp, ops_tail1));
452 ops2 = ops_tail2.split_first();
453 } else if l1 == n2 {
454 ops1 = ops_tail1.split_first();
455 ops2 = ops_tail2.split_first();
456 } else {
457 tmp = Box::new(Operation::Delete(n2 - l1));
458 ops1 = ops_tail1.split_first();
459 ops2 = Some((&tmp, ops_tail2));
460 }
461 }
462 // I,R
463 (
464 Some((Operation::Insert(s1), ops_tail1)),
465 Some((&Operation::Retain(n2), ops_tail2)),
466 ) => {
467 let l1 = s1.chars().count();
468 if l1 > n2 {
469 let chars = &mut s1.chars();
470 composed.insert(chars_take(chars, n2));
471 tmp = Box::new(Operation::Insert(chars_take(chars, l1 - n2)));
472 ops1 = Some((&tmp, ops_tail1));
473 ops2 = ops_tail2.split_first();
474 } else if l1 == n2 {
475 composed.insert(s1.clone());
476 ops1 = ops_tail1.split_first();
477 ops2 = ops_tail2.split_first();
478 } else {
479 composed.insert(s1.clone());
480 tmp = Box::new(Operation::Retain(n2 - l1));
481 ops2 = Some((&tmp, ops_tail2));
482 ops1 = ops_tail1.split_first();
483 }
484 }
485 // R,D
486 (
487 Some((&Operation::Retain(n1), ops_tail1)),
488 Some((&Operation::Delete(n2), ops_tail2)),
489 ) => {
490 if n1 > n2 {
491 composed.delete(n2);
492 tmp = Box::new(Operation::Retain(n1 - n2));
493 ops1 = Some((&tmp, ops_tail1));
494 ops2 = ops_tail2.split_first();
495 } else if n1 == n2 {
496 composed.delete(n2);
497 ops1 = ops_tail1.split_first();
498 ops2 = ops_tail2.split_first();
499 } else {
500 composed.delete(n1);
501 tmp = Box::new(Operation::Delete(n2 - n1));
502 ops2 = Some((&tmp, ops_tail2));
503 ops1 = ops_tail1.split_first();
504 }
505 }
506 }
507 }
508 Ok(composed)
509 }
510
511 /// 获取起始游标
512 fn first_cursor(&self) -> usize {
513 if let Some(&Operation::Retain(n)) = self.ops.first() {
514 return n;
515 }
516 return 0;
517 }
518
519 /// 如果当前操作是简单操作,则返回这个简单操作的内容,否者返回 None。
520 /// 简单操作指的是:只进行了一次或零次 Insert/Delete 操作
521 fn get_simple_operation(&self) -> Option<&Operation> {
522 match self.ops.as_slice() {
523 // [_] => [0]
524 [first] => Some(first),
525 // [R, _] => [1]
526 [Operation::Retain(_), second] => Some(second),
527 // [I|D, R] => [0]
528 [first, Operation::Retain(_)] => Some(first),
529 // [R, _, R] => [1]
530 [Operation::Retain(_), second, Operation::Retain(_)] => Some(second),
531 _ => None,
532 }
533 }
534
535 /// 当使用 ctrl-z 撤消最近的更改时,希望程序不会撤消每一次击键,而是撤消一口气写下的最后一句话或通过按住退格键所做的删除。
536 /// 这可以通过在将撤消栈上的操作进行 compose 来实现。 这个方法可以帮助决定是否应该组合两个操作。
537 /// 如果操作是 `连续的插入操作` 或 `连续的删除操作`,则返回 true。
538 /// 可能希望包括其他因素,例如自上次更改决定以来的时间。
539 /// # Example
540 /// ```
541 /// use ot_rs::core::TextOperation;
542 /// let mut ops1: TextOperation;
543 /// let mut ops2: TextOperation;
544 /// // noop;I / I;noop
545 /// ops1 = TextOperation::new();
546 /// ops1.retain(3);
547 /// ops2 = TextOperation::new();
548 /// ops2.retain(1).insert("xxx").retain(2);
549 /// assert!(ops1.should_be_composed_with(&ops2));
550 /// assert!(ops2.should_be_composed_with(&ops1));
551 /// // I;I 正常输入
552 /// ops1 = TextOperation::new();
553 /// ops1.retain(1).insert("a").retain(2);
554 /// ops2 = TextOperation::new();
555 /// ops2.retain(2).insert("b").retain(2);
556 /// assert!(ops1.should_be_composed_with(&ops2));
557 /// ops1.delete(3);
558 /// assert!(!ops1.should_be_composed_with(&ops2));
559 /// // I;I 插入后光标发生变化
560 /// ops1 = TextOperation::new();
561 /// ops1.retain(1).insert("b").retain(2);
562 /// ops2 = TextOperation::new();
563 /// ops2.retain(1).insert("a").retain(3);
564 /// assert!(!ops1.should_be_composed_with(&ops2));
565 /// // D;D 退格键方式
566 /// ops1 = TextOperation::new();
567 /// ops1.retain(4).delete(3).retain(10);
568 /// ops2 = TextOperation::new();
569 /// ops2.retain(2).delete(2).retain(10);
570 /// assert!(ops1.should_be_composed_with(&ops2));
571 /// // D;D delete键方式
572 /// ops2 = TextOperation::new();
573 /// ops2.retain(4).delete(7).retain(3);
574 /// assert!(ops1.should_be_composed_with(&ops2));
575 /// // D;D 不连续的删除
576 /// ops2 = TextOperation::new();
577 /// ops2.retain(2).delete(9).retain(3);
578 /// assert!(!ops1.should_be_composed_with(&ops2));
579 /// ```
580 pub fn should_be_composed_with(&self, other: &TextOperation) -> bool {
581 // 无影响的操作,可以合并
582 if self.is_noop() || other.is_noop() {
583 return true;
584 }
585 let (a_first_cursor, b_first_cursor) = (self.first_cursor(), other.first_cursor());
586 let (a_sample, b_sample) = (self.get_simple_operation(), other.get_simple_operation());
587 // 只要一个是非简单操作,则不可以合并
588 if a_sample.is_none() || b_sample.is_none() {
589 return false;
590 }
591 match (a_sample, b_sample, a_first_cursor, b_first_cursor) {
592 // I, I - 保证后插入的在之前插入的后方进行插入
593 (Some(Operation::Insert(str)), Some(Operation::Insert(_)), _, _) => {
594 return str.chars().count() + a_first_cursor == b_first_cursor; // 连续输入两个字符
595 }
596 // D, D
597 (Some(&Operation::Delete(_)), Some(&Operation::Delete(dn2)), _, _) => {
598 return b_first_cursor as i64 + dn2 as i64 == a_first_cursor as i64 // 按两下退格的场景
599 || a_first_cursor == b_first_cursor; // 按两下 delete 键的场景
600 }
601 // 其他情况
602 _ => false,
603 }
604 }
605
606 /// 决定两个操作如果被 invert 是否应该相互组合,即 `should_be_composed_with_inverted(a, b) = should_be_composed_with_inverted(b^{-1}, a^{-1})`
607 pub fn should_be_composed_with_inverted(&self, other: &TextOperation) -> bool {
608 // 无影响的操作,可以合并
609 if self.is_noop() || other.is_noop() {
610 return true;
611 }
612 let (a_first_cursor, b_first_cursor) = (self.first_cursor(), other.first_cursor());
613 let (a_sample, b_sample) = (self.get_simple_operation(), other.get_simple_operation());
614 // 只要一个是非简单操作,则不可以合并
615 if a_sample.is_none() || b_sample.is_none() {
616 return false;
617 }
618 match (a_sample, b_sample, a_first_cursor, b_first_cursor) {
619 // I, I - 因为是逆,所以原操作是 Delete
620 (Some(Operation::Insert(str)), Some(Operation::Insert(_)), _, _) => {
621 return a_first_cursor + str.chars().count() == b_first_cursor
622 || a_first_cursor == b_first_cursor;
623 }
624 // D, D - 因为是逆,所以原操作是 Insert
625 (Some(&Operation::Delete(_)), Some(&Operation::Delete(dn2)), _, _) => {
626 return b_first_cursor as i64 - dn2 as i64 == a_first_cursor as i64
627 }
628 // 其他情况
629 _ => false,
630 }
631 }
632
633 /// 这个函数是 OT 算法的核心。
634 /// 转换两个基于同一版本 S 的操作 A 和 B,返回 A' 和 B',使其满足
635 /// `apply(apply(S, A), B') = apply(apply(S, B), A')`。
636 pub fn transform(
637 &self,
638 operation2: &TextOperation,
639 ) -> Result<(TextOperation, TextOperation), OperationError> {
640 let operation1 = self;
641 if operation1.base_length != operation2.base_length {
642 return Err(OperationError::TransformBaseDifferent);
643 }
644
645 let mut tmp: Box<Operation>; // 修复 rust 生命周期检测
646 let (mut operation1prime, mut operation2prime) =
647 (TextOperation::new(), TextOperation::new());
648
649 let mut ops1 = self.ops.split_first();
650 let mut ops2 = operation2.ops.split_first();
651 // 和 compose 方法类似
652 // 思路大概是:
653 // 设置两个游标,同时遍历 ops1,ops2;
654 // 每一轮迭代,都要保证游标,在 S 的位置是一致的
655 // 因此我们只需按照递归的思路,思考初始的状态的9种组合即可。
656 // 全部枚举出来后,进行剪枝,就可以得到如下的算法
657 loop {
658 match (ops1, ops2) {
659 (None, None) => break,
660 // 如下两种情况:只要有一方是 Insert,这一方面方的 Prime 就跳过,量一方的 Prime 就插入
661 // (3 种情况) I, _
662 (Some((Operation::Insert(str1), tail1)), _) => {
663 operation1prime.insert(str1.clone());
664 operation2prime.retain(str1.chars().count());
665 ops1 = tail1.split_first();
666 }
667 // (2 种情况) _, I
668 (_, Some((Operation::Insert(str2), tail2))) => {
669 operation1prime.retain(str2.chars().count());
670 operation2prime.insert(str2.clone());
671 ops2 = tail2.split_first();
672 }
673 // 异常:只要有一方完成另一方未完成,则报错
674 (None, _) => return Err(OperationError::ComposeFirstTooShort),
675 (_, None) => return Err(OperationError::ComposeFirstTooLong),
676 // (1 种情况) R, R
677 (Some((&Operation::Retain(n1), tail1)), Some((&Operation::Retain(n2), tail2))) => {
678 let min_n = if n1 > n2 {
679 tmp = Box::new(Operation::Retain(n1 - n2));
680 ops1 = Some((&tmp, tail1));
681 ops2 = tail2.split_first();
682 n2
683 } else if n1 == n2 {
684 ops1 = tail1.split_first();
685 ops2 = tail2.split_first();
686 n2
687 } else {
688 tmp = Box::new(Operation::Retain(n2 - n1));
689 ops1 = tail1.split_first();
690 ops2 = Some((&tmp, tail2));
691 n1
692 };
693 operation1prime.retain(min_n);
694 operation2prime.retain(min_n);
695 }
696 // (1 种情况) D, D
697 // 同时删除,我们只需要将删除长的保留后面部分,删除短的直接跳过
698 (Some((&Operation::Delete(n1), tail1)), Some((&Operation::Delete(n2), tail2))) => {
699 if n1 > n2 {
700 tmp = Box::new(Operation::Delete(n1 - n2));
701 ops1 = Some((&tmp, tail1));
702 ops2 = tail2.split_first();
703 } else if n1 == n2 {
704 ops1 = tail1.split_first();
705 ops2 = tail2.split_first();
706 } else {
707 tmp = Box::new(Operation::Delete(n2 - n1));
708 ops1 = tail1.split_first();
709 ops2 = Some((&tmp, tail2));
710 }
711 }
712 // 接下来两种情况是 D,R 和 R,D
713 // (1 种情况) D, R
714 (Some((&Operation::Delete(n1), tail1)), Some((&Operation::Retain(n2), tail2))) => {
715 let min_n = if n1 > n2 {
716 tmp = Box::new(Operation::Delete(n1 - n2));
717 ops1 = Some((&tmp, tail1));
718 ops2 = tail2.split_first();
719 n2
720 } else if n1 == n2 {
721 ops1 = tail1.split_first();
722 ops2 = tail2.split_first();
723 n2
724 } else {
725 tmp = Box::new(Operation::Retain(n2 - n1));
726 ops1 = tail1.split_first();
727 ops2 = Some((&tmp, tail2));
728 n1
729 };
730 operation1prime.delete(min_n);
731 }
732 // (1 种情况) R, D
733 (Some((&Operation::Retain(n1), tail1)), Some((&Operation::Delete(n2), tail2))) => {
734 let min_n = if n1 > n2 {
735 tmp = Box::new(Operation::Retain(n1 - n2));
736 ops1 = Some((&tmp, tail1));
737 ops2 = tail2.split_first();
738 n2
739 } else if n1 == n2 {
740 ops1 = tail1.split_first();
741 ops2 = tail2.split_first();
742 n2
743 } else {
744 tmp = Box::new(Operation::Delete(n2 - n1));
745 ops1 = tail1.split_first();
746 ops2 = Some((&tmp, tail2));
747 n1
748 };
749 operation2prime.delete(min_n);
750 } // _ => return Err(OperationError::TransformNotCompatible),
751 }
752 }
753 return Ok((operation1prime, operation2prime));
754 }
755}
756
757impl Default for TextOperation {
758 fn default() -> Self {
759 Self::new()
760 }
761}
762
763fn chars_take(chars: &mut Chars, n: usize) -> String {
764 (0..n).map(|_| chars.next().unwrap()).collect::<String>()
765}
766
767fn chars_tail(chars: &mut Chars, skip: usize) -> String {
768 chars_skip(chars, skip);
769 chars.collect::<String>()
770}
771
772fn chars_skip(chars: &mut Chars, n: usize) {
773 (0..n).for_each(|_| {
774 chars.next().unwrap();
775 })
776}
777
778#[cfg(test)]
779mod tests {
780
781 use crate::core::operation::Operation;
782
783 use super::TextOperation;
784 use rand::{self, Rng};
785
786 const CHARSET: [char; 10] = ['a', 'b', 'c', '1', '2', '3', '中', '文', '😄', '😂'];
787 // const RAND_TEST_COUNT: usize = 500;
788 const RAND_TEST_COUNT: usize = 100;
789
790 fn random_string(n: usize) -> String {
791 let mut rng: rand::prelude::ThreadRng = rand::thread_rng();
792 (0..n)
793 .map(|_| rng.gen_range(0..CHARSET.len()))
794 .map(|i| CHARSET[i])
795 .collect()
796 }
797
798 fn random_operation<T: Into<String>>(base: T) -> TextOperation {
799 let base = base.into();
800 let mut ops = TextOperation::new();
801 let mut rng: rand::prelude::ThreadRng = rand::thread_rng();
802 loop {
803 let left = base.chars().count() - ops.base_length;
804 if left == 0 {
805 break;
806 }
807 let r = rng.gen_range(0.0..1.0);
808 let l = rng.gen_range(1..=left);
809 if r < 0.2 {
810 ops.insert(random_string(l));
811 } else if r < 0.4 {
812 ops.delete(l);
813 } else {
814 ops.retain(l);
815 }
816 }
817 if rng.gen_range(0.0..1.0) < 0.3 {
818 ops.insert(random_string(10));
819 }
820 ops
821 }
822
823 fn run_n(n: usize, f: fn() -> ()) {
824 for _ in 0..n {
825 f();
826 }
827 }
828
829 #[test]
830 fn test_apply() {
831 run_n(RAND_TEST_COUNT, || {
832 let base = random_string(50);
833 let ops = random_operation(&base);
834 let after = ops.apply(&base).unwrap();
835 println!(" {} \n->\n {} \nby\n {}", &base, &after, ops.to_string());
836 assert_eq!(base.chars().count(), ops.base_length);
837 assert_eq!(after.chars().count(), ops.after_length);
838 })
839 }
840
841 #[test]
842 fn test_invert() {
843 run_n(RAND_TEST_COUNT, || {
844 let base = random_string(50);
845 let ops = random_operation(&base);
846 assert_eq!(
847 base,
848 ops.invert(&base)
849 .unwrap()
850 .apply(ops.apply(&base).unwrap())
851 .unwrap()
852 );
853 })
854 }
855
856 #[test]
857 fn test_compose() {
858 run_n(RAND_TEST_COUNT, || {
859 // after2 = apply(apply(base, ops1), ops2)
860 let base = random_string(50);
861 let ops1 = random_operation(&base);
862 let after1 = ops1.apply(&base).unwrap();
863 let ops2 = random_operation(&after1);
864 let after2 = ops2.apply(&after1).unwrap();
865 // ops1 + ops2 = compose_ops;
866 let compose_ops = ops1.compose(&ops2).unwrap();
867 assert_eq!(after2, compose_ops.apply(&base).unwrap());
868 })
869 }
870
871 #[test]
872 fn test_first_cursor() {
873 assert_eq!(0, TextOperation::new().first_cursor());
874 assert_eq!(0, TextOperation::new().delete(1).first_cursor());
875 assert_eq!(1, TextOperation::new().retain(1).first_cursor());
876 assert_eq!(0, TextOperation::new().insert("a").first_cursor());
877 }
878
879 #[test]
880 fn test_get_simple_operation() {
881 assert_eq!(None, TextOperation::new().get_simple_operation());
882 assert_eq!(
883 &Operation::Delete(1),
884 TextOperation::new()
885 .delete(1)
886 .get_simple_operation()
887 .unwrap()
888 );
889 assert_eq!(
890 &Operation::Retain(1),
891 TextOperation::new()
892 .retain(1)
893 .get_simple_operation()
894 .unwrap()
895 );
896 assert_eq!(
897 &Operation::Insert("abc".to_string()),
898 TextOperation::new()
899 .retain(1)
900 .insert("abc")
901 .retain(1)
902 .get_simple_operation()
903 .unwrap()
904 );
905 }
906
907 #[test]
908 fn should_be_composed_with_inverted() {
909 run_n(RAND_TEST_COUNT, || {
910 // invariant: should_be_composed_with_inverted(a, b) = should_be_composed_with_inverted(b^{-1}, a^{-1})
911 let base = random_string(50);
912 let ops1 = random_operation(&base);
913 let ops1_inverted = ops1.invert(&base).unwrap();
914 let after1 = ops1.apply(&base).unwrap();
915
916 let ops2 = random_operation(&after1);
917 let ops2_inverted = ops2.invert(&after1).unwrap();
918 assert_eq!(
919 ops1.should_be_composed_with(&ops2),
920 ops2_inverted.should_be_composed_with_inverted(&ops1_inverted),
921 );
922 })
923 }
924
925 #[test]
926 fn should_transform() {
927 // transform(a, b) => ('a, 'b)
928 // apply(apply(s, a), a') = apply(apply(s, b), a')
929 // compose(a, b') = compose(b, a')
930 run_n(RAND_TEST_COUNT, || {
931 let base = random_string(50);
932 let sa = random_operation(&base);
933 let sb = random_operation(&base);
934 let (sa_prime, sb_prime) = TextOperation::transform(&sa, &sb).unwrap();
935 let ab_prime = sa.compose(&sb_prime).unwrap();
936 let ba_prime = sb.compose(&sa_prime).unwrap();
937 let sa_sb_prime_after = ab_prime.apply(&base);
938 let sb_sa_prime_after = ba_prime.apply(&base);
939 assert_eq!(ab_prime, ba_prime);
940 assert_eq!(sa_sb_prime_after, sb_sa_prime_after);
941 });
942 }
943}