dx_forge/crdt/
document.rs

1use 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    /// Automerge document for CRDT operations
14    pub doc: Arc<RwLock<AutoCommit>>,
15    /// Rope for efficient text editing
16    pub rope: Arc<RwLock<Rope>>,
17    /// Lamport timestamp for ordering
18    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                // Update CRDT
48                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                // Update CRDT
63                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                // Update CRDT
83                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    /// Get the current position from a stable anchor
115    pub fn resolve_anchor(&self, _stable_id: &str) -> Option<(usize, usize)> {
116        // This would use the CRDT's operation history to resolve
117        // the current position of an anchor
118        // For now, simplified implementation
119        None
120    }
121}