dx_forge/crdt/
document.rs1use anyhow::Result;
2use automerge::{transaction::Transactable, AutoCommit, ReadDoc, ROOT};
3use parking_lot::RwLock;
4use ropey::Rope;
5use std::path::PathBuf;
6use std::sync::Arc;
7
8use super::operations::{Operation, OperationType, Position};
9
10#[allow(dead_code)]
11pub struct CrdtDocument {
12 pub path: PathBuf,
13 pub doc: Arc<RwLock<AutoCommit>>,
15 pub rope: Arc<RwLock<Rope>>,
17 pub lamport: Arc<parking_lot::Mutex<u64>>,
19}
20
21#[allow(dead_code)]
22impl CrdtDocument {
23 pub fn new(path: PathBuf, initial_content: &str) -> Self {
24 let mut doc = AutoCommit::new();
25 doc.put(ROOT, "content", initial_content).unwrap();
26
27 Self {
28 path,
29 doc: Arc::new(RwLock::new(doc)),
30 rope: Arc::new(RwLock::new(Rope::from_str(initial_content))),
31 lamport: Arc::new(parking_lot::Mutex::new(0)),
32 }
33 }
34
35 pub fn apply_operation(&self, op: &Operation) -> Result<()> {
36 let mut lamport = self.lamport.lock();
37 *lamport += 1;
38
39 match &op.op_type {
40 OperationType::Insert {
41 position, content, ..
42 } => {
43 let mut rope = self.rope.write();
44 let char_idx = self.line_col_to_char(&rope, position.line, position.column);
45 rope.insert(char_idx, content);
46
47 let mut doc = self.doc.write();
49 if let Some((value, _)) = doc.get(ROOT, "content")? {
50 let current: String = value.to_string();
51 let mut chars: Vec<char> = current.chars().collect();
52 chars.splice(char_idx..char_idx, content.chars());
53 doc.put(ROOT, "content", chars.iter().collect::<String>())?;
54 }
55 }
56
57 OperationType::Delete { position, length } => {
58 let mut rope = self.rope.write();
59 let char_idx = self.line_col_to_char(&rope, position.line, position.column);
60 rope.remove(char_idx..char_idx + length);
61
62 let mut doc = self.doc.write();
64 if let Some((value, _)) = doc.get(ROOT, "content")? {
65 let current: String = value.to_string();
66 let mut chars: Vec<char> = current.chars().collect();
67 chars.drain(char_idx..char_idx + length);
68 doc.put(ROOT, "content", chars.iter().collect::<String>())?;
69 }
70 }
71
72 OperationType::Replace {
73 position,
74 old_content,
75 new_content,
76 } => {
77 let mut rope = self.rope.write();
78 let char_idx = self.line_col_to_char(&rope, position.line, position.column);
79 rope.remove(char_idx..char_idx + old_content.len());
80 rope.insert(char_idx, new_content);
81
82 let mut doc = self.doc.write();
84 if let Some((value, _)) = doc.get(ROOT, "content")? {
85 let current: String = value.to_string();
86 let mut chars: Vec<char> = current.chars().collect();
87 chars.splice(char_idx..char_idx + old_content.len(), new_content.chars());
88 doc.put(ROOT, "content", chars.iter().collect::<String>())?;
89 }
90 }
91
92 _ => {}
93 }
94
95 Ok(())
96 }
97
98 pub fn get_content(&self) -> String {
99 self.rope.read().to_string()
100 }
101
102 pub fn create_position(&self, line: usize, column: usize, actor_id: String) -> Position {
103 let rope = self.rope.read();
104 let offset = self.line_col_to_char(&rope, line, column);
105 let lamport = *self.lamport.lock();
106
107 Position::new(line, column, offset, actor_id, lamport)
108 }
109
110 fn line_col_to_char(&self, rope: &Rope, line: usize, col: usize) -> usize {
111 rope.line_to_char(line.saturating_sub(1)) + col.saturating_sub(1)
112 }
113
114 pub fn resolve_anchor(&self, _stable_id: &str) -> Option<(usize, usize)> {
116 None
120 }
121}