1use mdcs_core::lattice::Lattice;
4use mdcs_db::{
5 json_crdt::{JsonCrdt, JsonPath, JsonValue},
6 rga_text::RGAText,
7 rich_text::{MarkType, RichText},
8};
9use tokio::sync::broadcast;
10
11#[derive(Clone, Debug)]
13pub enum DocEvent {
14 Insert { position: usize, text: String },
16 Delete { position: usize, length: usize },
18 RemoteUpdate,
20}
21
22pub trait CollaborativeDoc {
24 fn id(&self) -> &str;
26
27 fn replica_id(&self) -> &str;
29
30 fn subscribe(&self) -> broadcast::Receiver<DocEvent>;
32
33 fn take_pending_deltas(&mut self) -> Vec<Vec<u8>>;
35
36 fn apply_remote(&mut self, delta: &[u8]);
38}
39
40#[derive(Clone)]
42pub struct TextDoc {
43 id: String,
44 replica_id: String,
45 text: RGAText,
46 #[allow(dead_code)]
47 event_tx: broadcast::Sender<DocEvent>,
48 pending_deltas: Vec<Vec<u8>>,
49}
50
51impl TextDoc {
52 pub fn new(id: impl Into<String>, replica_id: impl Into<String>) -> Self {
54 let replica_id = replica_id.into();
55 let (event_tx, _) = broadcast::channel(100);
56
57 Self {
58 id: id.into(),
59 replica_id: replica_id.clone(),
60 text: RGAText::new(&replica_id),
61 event_tx,
62 pending_deltas: Vec::new(),
63 }
64 }
65
66 pub fn insert(&mut self, position: usize, text: &str) {
68 self.text.insert(position, text);
69 let _ = self.event_tx.send(DocEvent::Insert {
70 position,
71 text: text.to_string(),
72 });
73 }
74
75 pub fn delete(&mut self, position: usize, length: usize) {
77 self.text.delete(position, length);
78 let _ = self.event_tx.send(DocEvent::Delete { position, length });
79 }
80
81 pub fn get_text(&self) -> String {
83 self.text.to_string()
84 }
85
86 pub fn len(&self) -> usize {
88 self.text.len()
89 }
90
91 pub fn is_empty(&self) -> bool {
93 self.text.is_empty()
94 }
95
96 pub fn merge(&mut self, other: &TextDoc) {
99 self.text = self.text.join(&other.text);
100 let _ = self.event_tx.send(DocEvent::RemoteUpdate);
101 }
102
103 pub fn clone_state(&self) -> TextDoc {
105 TextDoc {
106 id: self.id.clone(),
107 replica_id: self.replica_id.clone(),
108 text: self.text.clone(),
109 event_tx: self.event_tx.clone(),
110 pending_deltas: Vec::new(),
111 }
112 }
113}
114
115impl CollaborativeDoc for TextDoc {
116 fn id(&self) -> &str {
117 &self.id
118 }
119
120 fn replica_id(&self) -> &str {
121 &self.replica_id
122 }
123
124 fn subscribe(&self) -> broadcast::Receiver<DocEvent> {
125 self.event_tx.subscribe()
126 }
127
128 fn take_pending_deltas(&mut self) -> Vec<Vec<u8>> {
129 std::mem::take(&mut self.pending_deltas)
130 }
131
132 fn apply_remote(&mut self, _delta: &[u8]) {
133 let _ = self.event_tx.send(DocEvent::RemoteUpdate);
135 }
136}
137
138#[derive(Clone)]
140pub struct RichTextDoc {
141 id: String,
142 replica_id: String,
143 text: RichText,
144 #[allow(dead_code)]
145 event_tx: broadcast::Sender<DocEvent>,
146 pending_deltas: Vec<Vec<u8>>,
147}
148
149impl RichTextDoc {
150 pub fn new(id: impl Into<String>, replica_id: impl Into<String>) -> Self {
152 let replica_id = replica_id.into();
153 let (event_tx, _) = broadcast::channel(100);
154
155 Self {
156 id: id.into(),
157 replica_id: replica_id.clone(),
158 text: RichText::new(&replica_id),
159 event_tx,
160 pending_deltas: Vec::new(),
161 }
162 }
163
164 pub fn insert(&mut self, position: usize, text: &str) {
166 self.text.insert(position, text);
167 let _ = self.event_tx.send(DocEvent::Insert {
168 position,
169 text: text.to_string(),
170 });
171 }
172
173 pub fn delete(&mut self, position: usize, length: usize) {
175 self.text.delete(position, length);
176 let _ = self.event_tx.send(DocEvent::Delete { position, length });
177 }
178
179 pub fn format(&mut self, start: usize, end: usize, mark: MarkType) {
181 self.text.add_mark(start, end, mark);
182 }
183
184 pub fn unformat_by_id(&mut self, mark_id: &mdcs_db::rich_text::MarkId) {
186 self.text.remove_mark(mark_id);
187 }
188
189 pub fn get_text(&self) -> String {
191 self.text.to_string()
192 }
193
194 pub fn get_content(&self) -> String {
197 self.text.to_string()
198 }
199
200 pub fn len(&self) -> usize {
202 self.text.len()
203 }
204
205 pub fn is_empty(&self) -> bool {
207 self.text.is_empty()
208 }
209
210 pub fn merge(&mut self, other: &RichTextDoc) {
213 self.text = self.text.join(&other.text);
214 let _ = self.event_tx.send(DocEvent::RemoteUpdate);
215 }
216
217 pub fn clone_state(&self) -> RichTextDoc {
219 RichTextDoc {
220 id: self.id.clone(),
221 replica_id: self.replica_id.clone(),
222 text: self.text.clone(),
223 event_tx: self.event_tx.clone(),
224 pending_deltas: Vec::new(),
225 }
226 }
227}
228
229impl CollaborativeDoc for RichTextDoc {
230 fn id(&self) -> &str {
231 &self.id
232 }
233
234 fn replica_id(&self) -> &str {
235 &self.replica_id
236 }
237
238 fn subscribe(&self) -> broadcast::Receiver<DocEvent> {
239 self.event_tx.subscribe()
240 }
241
242 fn take_pending_deltas(&mut self) -> Vec<Vec<u8>> {
243 std::mem::take(&mut self.pending_deltas)
244 }
245
246 fn apply_remote(&mut self, _delta: &[u8]) {
247 let _ = self.event_tx.send(DocEvent::RemoteUpdate);
248 }
249}
250
251#[derive(Clone)]
253pub struct JsonDoc {
254 id: String,
255 replica_id: String,
256 doc: JsonCrdt,
257 #[allow(dead_code)]
258 event_tx: broadcast::Sender<DocEvent>,
259 pending_deltas: Vec<Vec<u8>>,
260}
261
262impl JsonDoc {
263 pub fn new(id: impl Into<String>, replica_id: impl Into<String>) -> Self {
265 let replica_id = replica_id.into();
266 let (event_tx, _) = broadcast::channel(100);
267
268 Self {
269 id: id.into(),
270 replica_id: replica_id.clone(),
271 doc: JsonCrdt::new(&replica_id),
272 event_tx,
273 pending_deltas: Vec::new(),
274 }
275 }
276
277 pub fn set(&mut self, path: &str, value: JsonValue) {
279 let json_path = JsonPath::parse(path);
280 let _ = self.doc.set(&json_path, value);
281 }
282
283 pub fn get(&self, path: &str) -> Option<JsonValue> {
285 let json_path = JsonPath::parse(path);
286 self.doc.get(&json_path).cloned()
287 }
288
289 pub fn delete(&mut self, path: &str) {
291 let json_path = JsonPath::parse(path);
292 let _ = self.doc.delete(&json_path);
293 }
294
295 pub fn root(&self) -> serde_json::Value {
297 self.doc.to_json()
298 }
299
300 pub fn keys(&self) -> Vec<String> {
302 self.doc.keys()
303 }
304
305 pub fn merge(&mut self, other: &JsonDoc) {
308 self.doc = self.doc.join(&other.doc);
309 let _ = self.event_tx.send(DocEvent::RemoteUpdate);
310 }
311
312 pub fn clone_state(&self) -> JsonDoc {
314 JsonDoc {
315 id: self.id.clone(),
316 replica_id: self.replica_id.clone(),
317 doc: self.doc.clone(),
318 event_tx: self.event_tx.clone(),
319 pending_deltas: Vec::new(),
320 }
321 }
322}
323
324impl CollaborativeDoc for JsonDoc {
325 fn id(&self) -> &str {
326 &self.id
327 }
328
329 fn replica_id(&self) -> &str {
330 &self.replica_id
331 }
332
333 fn subscribe(&self) -> broadcast::Receiver<DocEvent> {
334 self.event_tx.subscribe()
335 }
336
337 fn take_pending_deltas(&mut self) -> Vec<Vec<u8>> {
338 std::mem::take(&mut self.pending_deltas)
339 }
340
341 fn apply_remote(&mut self, _delta: &[u8]) {
342 let _ = self.event_tx.send(DocEvent::RemoteUpdate);
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[test]
351 fn test_text_doc() {
352 let mut doc = TextDoc::new("doc-1", "replica-1");
353 doc.insert(0, "Hello");
354 doc.insert(5, " World");
355
356 assert_eq!(doc.get_text(), "Hello World");
357 assert_eq!(doc.len(), 11);
358 }
359
360 #[test]
361 fn test_rich_text_doc() {
362 let mut doc = RichTextDoc::new("doc-1", "replica-1");
363 doc.insert(0, "Hello World");
364 doc.format(0, 5, MarkType::Bold);
365
366 assert_eq!(doc.get_text(), "Hello World");
367 }
368
369 #[test]
370 fn test_json_doc() {
371 let mut doc = JsonDoc::new("doc-1", "replica-1");
372 doc.set("name", JsonValue::String("Alice".to_string()));
373 doc.set("age", JsonValue::Float(30.0));
374
375 assert_eq!(
376 doc.get("name"),
377 Some(JsonValue::String("Alice".to_string()))
378 );
379 }
380}