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>;
34
35 fn take_pending_deltas(&mut self) -> Vec<Vec<u8>>;
37
38 fn apply_remote(&mut self, delta: &[u8]);
40}
41
42#[derive(Clone)]
44pub struct TextDoc {
45 id: String,
46 replica_id: String,
47 text: RGAText,
48 #[allow(dead_code)]
49 event_tx: broadcast::Sender<DocEvent>,
50 pending_deltas: Vec<Vec<u8>>,
51}
52
53impl TextDoc {
54 pub fn new(id: impl Into<String>, replica_id: impl Into<String>) -> Self {
56 let replica_id = replica_id.into();
57 let (event_tx, _) = broadcast::channel(100);
58
59 Self {
60 id: id.into(),
61 replica_id: replica_id.clone(),
62 text: RGAText::new(&replica_id),
63 event_tx,
64 pending_deltas: Vec::new(),
65 }
66 }
67
68 pub fn insert(&mut self, position: usize, text: &str) {
70 self.text.insert(position, text);
71 let _ = self.event_tx.send(DocEvent::Insert {
72 position,
73 text: text.to_string(),
74 });
75 }
76
77 pub fn delete(&mut self, position: usize, length: usize) {
79 self.text.delete(position, length);
80 let _ = self.event_tx.send(DocEvent::Delete { position, length });
81 }
82
83 pub fn get_text(&self) -> String {
85 self.text.to_string()
86 }
87
88 pub fn len(&self) -> usize {
90 self.text.len()
91 }
92
93 pub fn is_empty(&self) -> bool {
95 self.text.is_empty()
96 }
97
98 pub fn merge(&mut self, other: &TextDoc) {
102 self.text = self.text.join(&other.text);
103 let _ = self.event_tx.send(DocEvent::RemoteUpdate);
104 }
105
106 pub fn clone_state(&self) -> TextDoc {
110 TextDoc {
111 id: self.id.clone(),
112 replica_id: self.replica_id.clone(),
113 text: self.text.clone(),
114 event_tx: self.event_tx.clone(),
115 pending_deltas: Vec::new(),
116 }
117 }
118}
119
120impl CollaborativeDoc for TextDoc {
121 fn id(&self) -> &str {
122 &self.id
123 }
124
125 fn replica_id(&self) -> &str {
126 &self.replica_id
127 }
128
129 fn subscribe(&self) -> broadcast::Receiver<DocEvent> {
130 self.event_tx.subscribe()
131 }
132
133 fn take_pending_deltas(&mut self) -> Vec<Vec<u8>> {
134 std::mem::take(&mut self.pending_deltas)
135 }
136
137 fn apply_remote(&mut self, _delta: &[u8]) {
138 let _ = self.event_tx.send(DocEvent::RemoteUpdate);
140 }
141}
142
143#[derive(Clone)]
145pub struct RichTextDoc {
146 id: String,
147 replica_id: String,
148 text: RichText,
149 #[allow(dead_code)]
150 event_tx: broadcast::Sender<DocEvent>,
151 pending_deltas: Vec<Vec<u8>>,
152}
153
154impl RichTextDoc {
155 pub fn new(id: impl Into<String>, replica_id: impl Into<String>) -> Self {
157 let replica_id = replica_id.into();
158 let (event_tx, _) = broadcast::channel(100);
159
160 Self {
161 id: id.into(),
162 replica_id: replica_id.clone(),
163 text: RichText::new(&replica_id),
164 event_tx,
165 pending_deltas: Vec::new(),
166 }
167 }
168
169 pub fn insert(&mut self, position: usize, text: &str) {
171 self.text.insert(position, text);
172 let _ = self.event_tx.send(DocEvent::Insert {
173 position,
174 text: text.to_string(),
175 });
176 }
177
178 pub fn delete(&mut self, position: usize, length: usize) {
180 self.text.delete(position, length);
181 let _ = self.event_tx.send(DocEvent::Delete { position, length });
182 }
183
184 pub fn format(&mut self, start: usize, end: usize, mark: MarkType) {
186 self.text.add_mark(start, end, mark);
187 }
188
189 pub fn unformat_by_id(&mut self, mark_id: &mdcs_db::rich_text::MarkId) {
191 self.text.remove_mark(mark_id);
192 }
193
194 pub fn get_text(&self) -> String {
196 self.text.to_string()
197 }
198
199 pub fn get_content(&self) -> String {
203 self.text.to_string()
204 }
205
206 pub fn len(&self) -> usize {
208 self.text.len()
209 }
210
211 pub fn is_empty(&self) -> bool {
213 self.text.is_empty()
214 }
215
216 pub fn merge(&mut self, other: &RichTextDoc) {
220 self.text = self.text.join(&other.text);
221 let _ = self.event_tx.send(DocEvent::RemoteUpdate);
222 }
223
224 pub fn clone_state(&self) -> RichTextDoc {
228 RichTextDoc {
229 id: self.id.clone(),
230 replica_id: self.replica_id.clone(),
231 text: self.text.clone(),
232 event_tx: self.event_tx.clone(),
233 pending_deltas: Vec::new(),
234 }
235 }
236}
237
238impl CollaborativeDoc for RichTextDoc {
239 fn id(&self) -> &str {
240 &self.id
241 }
242
243 fn replica_id(&self) -> &str {
244 &self.replica_id
245 }
246
247 fn subscribe(&self) -> broadcast::Receiver<DocEvent> {
248 self.event_tx.subscribe()
249 }
250
251 fn take_pending_deltas(&mut self) -> Vec<Vec<u8>> {
252 std::mem::take(&mut self.pending_deltas)
253 }
254
255 fn apply_remote(&mut self, _delta: &[u8]) {
256 let _ = self.event_tx.send(DocEvent::RemoteUpdate);
257 }
258}
259
260#[derive(Clone)]
262pub struct JsonDoc {
263 id: String,
264 replica_id: String,
265 doc: JsonCrdt,
266 #[allow(dead_code)]
267 event_tx: broadcast::Sender<DocEvent>,
268 pending_deltas: Vec<Vec<u8>>,
269}
270
271impl JsonDoc {
272 pub fn new(id: impl Into<String>, replica_id: impl Into<String>) -> Self {
274 let replica_id = replica_id.into();
275 let (event_tx, _) = broadcast::channel(100);
276
277 Self {
278 id: id.into(),
279 replica_id: replica_id.clone(),
280 doc: JsonCrdt::new(&replica_id),
281 event_tx,
282 pending_deltas: Vec::new(),
283 }
284 }
285
286 pub fn set(&mut self, path: &str, value: JsonValue) {
288 let json_path = JsonPath::parse(path);
289 let _ = self.doc.set(&json_path, value);
290 }
291
292 pub fn get(&self, path: &str) -> Option<JsonValue> {
294 let json_path = JsonPath::parse(path);
295 self.doc.get(&json_path).cloned()
296 }
297
298 pub fn delete(&mut self, path: &str) {
300 let json_path = JsonPath::parse(path);
301 let _ = self.doc.delete(&json_path);
302 }
303
304 pub fn root(&self) -> serde_json::Value {
306 self.doc.to_json()
307 }
308
309 pub fn keys(&self) -> Vec<String> {
311 self.doc.keys()
312 }
313
314 pub fn merge(&mut self, other: &JsonDoc) {
318 self.doc = self.doc.join(&other.doc);
319 let _ = self.event_tx.send(DocEvent::RemoteUpdate);
320 }
321
322 pub fn clone_state(&self) -> JsonDoc {
326 JsonDoc {
327 id: self.id.clone(),
328 replica_id: self.replica_id.clone(),
329 doc: self.doc.clone(),
330 event_tx: self.event_tx.clone(),
331 pending_deltas: Vec::new(),
332 }
333 }
334}
335
336impl CollaborativeDoc for JsonDoc {
337 fn id(&self) -> &str {
338 &self.id
339 }
340
341 fn replica_id(&self) -> &str {
342 &self.replica_id
343 }
344
345 fn subscribe(&self) -> broadcast::Receiver<DocEvent> {
346 self.event_tx.subscribe()
347 }
348
349 fn take_pending_deltas(&mut self) -> Vec<Vec<u8>> {
350 std::mem::take(&mut self.pending_deltas)
351 }
352
353 fn apply_remote(&mut self, _delta: &[u8]) {
354 let _ = self.event_tx.send(DocEvent::RemoteUpdate);
355 }
356}
357
358#[cfg(test)]
359mod tests {
360 use super::*;
361
362 #[test]
363 fn test_text_doc() {
364 let mut doc = TextDoc::new("doc-1", "replica-1");
365 doc.insert(0, "Hello");
366 doc.insert(5, " World");
367
368 assert_eq!(doc.get_text(), "Hello World");
369 assert_eq!(doc.len(), 11);
370 }
371
372 #[test]
373 fn test_rich_text_doc() {
374 let mut doc = RichTextDoc::new("doc-1", "replica-1");
375 doc.insert(0, "Hello World");
376 doc.format(0, 5, MarkType::Bold);
377
378 assert_eq!(doc.get_text(), "Hello World");
379 }
380
381 #[test]
382 fn test_json_doc() {
383 let mut doc = JsonDoc::new("doc-1", "replica-1");
384 doc.set("name", JsonValue::String("Alice".to_string()));
385 doc.set("age", JsonValue::Float(30.0));
386
387 assert_eq!(
388 doc.get("name"),
389 Some(JsonValue::String("Alice".to_string()))
390 );
391 }
392}