1use crate::types::EntityId;
4use flume::{Receiver, Sender, unbounded};
5use serde::Serialize;
6use std::{
7 sync::{Arc, Mutex, atomic::AtomicBool},
8 thread,
9};
10
11#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
12pub enum EntityEvent {
13 Created,
14 Updated,
15 Removed,
16}
17
18#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
19pub enum AllEvent {
20 Reset,
21}
22
23#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
24pub enum UndoRedoEvent {
25 Undone,
26 Redone,
27 BeginComposite,
28 EndComposite,
29 CancelComposite,
30}
31
32#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
33pub enum LongOperationEvent {
34 Started,
35 Progress,
36 Cancelled,
37 Completed,
38 Failed,
39}
40
41#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
42pub enum DirectAccessEntity {
43 All(AllEvent),
44
45 Root(EntityEvent),
46 Document(EntityEvent),
47 Frame(EntityEvent),
48 Block(EntityEvent),
49 InlineElement(EntityEvent),
50 List(EntityEvent),
51 Resource(EntityEvent),
52 Table(EntityEvent),
53 TableCell(EntityEvent),
54}
55
56#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
57pub enum DocumentEditingEvent {
58 InsertText,
59 DeleteText,
60 InsertBlock,
61 InsertImage,
62 InsertFrame,
63 InsertFormattedText,
64 CreateList,
65 InsertList,
66 InsertFragment,
67 InsertHtmlAtPosition,
68 InsertMarkdownAtPosition,
69 InsertTable,
70 RemoveTable,
71 InsertTableRow,
72 InsertTableColumn,
73 RemoveTableRow,
74 RemoveTableColumn,
75 MergeTableCells,
76 SplitTableCell,
77}
78
79#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
80pub enum DocumentFormattingEvent {
81 SetTextFormat,
82 MergeTextFormat,
83 SetBlockFormat,
84 SetFrameFormat,
85 SetTableFormat,
86 SetTableCellFormat,
87}
88
89#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
90pub enum DocumentIoEvent {
91 ImportPlainText,
92 ExportPlainText,
93 ImportMarkdown,
94 ExportMarkdown,
95 ImportHtml,
96 ExportHtml,
97 ExportLatex,
98 ExportDocx,
99}
100
101#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
102pub enum DocumentSearchEvent {
103 FindText,
104 FindAll,
105 ReplaceText,
106}
107
108#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
109pub enum DocumentInspectionEvent {
110 GetDocumentStats,
111 GetTextAtPosition,
112 GetBlockAtPosition,
113 ExtractFragment,
114}
115
116#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
117pub enum Origin {
118 DirectAccess(DirectAccessEntity),
119 UndoRedo(UndoRedoEvent),
120 LongOperation(LongOperationEvent),
121
122 DocumentEditing(DocumentEditingEvent),
123 DocumentFormatting(DocumentFormattingEvent),
124 DocumentIo(DocumentIoEvent),
125 DocumentSearch(DocumentSearchEvent),
126 DocumentInspection(DocumentInspectionEvent),
127}
128
129#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
130pub struct Event {
131 pub origin: Origin,
132 pub ids: Vec<EntityId>,
133 pub data: Option<String>,
134}
135
136impl Event {
137 pub fn origin_string(&self) -> String {
138 match &self.origin {
139 Origin::DirectAccess(entity) => match entity {
140 DirectAccessEntity::All(event) => format!("direct_access_all_{:?}", event),
141 DirectAccessEntity::Root(event) => format!("direct_access_root_{:?}", event),
143 DirectAccessEntity::Document(event) => {
144 format!("direct_access_document_{:?}", event)
145 }
146 DirectAccessEntity::Frame(event) => format!("direct_access_frame_{:?}", event),
147 DirectAccessEntity::Block(event) => format!("direct_access_block_{:?}", event),
148 DirectAccessEntity::InlineElement(event) => {
149 format!("direct_access_inline_element_{:?}", event)
150 }
151 DirectAccessEntity::List(event) => format!("direct_access_list_{:?}", event),
152 DirectAccessEntity::Resource(event) => {
153 format!("direct_access_resource_{:?}", event)
154 }
155 DirectAccessEntity::Table(event) => format!("direct_access_table_{:?}", event),
156 DirectAccessEntity::TableCell(event) => {
157 format!("direct_access_table_cell_{:?}", event)
158 }
159 },
160 Origin::UndoRedo(event) => format!("undo_redo_{:?}", event),
161 Origin::LongOperation(event) => format!("long_operation_{:?}", event),
162 Origin::DocumentEditing(event) => format!("document_editing_{:?}", event),
164 Origin::DocumentFormatting(event) => format!("document_formatting_{:?}", event),
165 Origin::DocumentIo(event) => format!("document_io_{:?}", event),
166 Origin::DocumentSearch(event) => format!("document_search_{:?}", event),
167 Origin::DocumentInspection(event) => format!("document_inspection_{:?}", event),
168 }
169 .to_lowercase()
170 }
171}
172pub struct EventBuffer {
181 buffering: bool,
182 pending: Vec<Event>,
183}
184
185impl EventBuffer {
186 pub fn new() -> Self {
187 Self {
188 buffering: false,
189 pending: Vec::new(),
190 }
191 }
192
193 pub fn begin_buffering(&mut self) {
195 self.buffering = true;
196 self.pending.clear();
197 }
198
199 pub fn push(&mut self, event: Event) {
204 if self.buffering {
205 self.pending.push(event);
206 }
207 }
208
209 pub fn flush(&mut self) -> Vec<Event> {
212 self.buffering = false;
213 std::mem::take(&mut self.pending)
214 }
215
216 pub fn discard(&mut self) {
218 self.buffering = false;
219 self.pending.clear();
220 }
221
222 pub fn is_buffering(&self) -> bool {
223 self.buffering
224 }
225}
226
227impl Default for EventBuffer {
228 fn default() -> Self {
229 Self::new()
230 }
231}
232
233pub type Queue = Arc<Mutex<Vec<Event>>>;
234
235pub struct EventHub {
237 sender: Sender<Event>,
238 receiver: Receiver<Event>,
239 queue: Queue,
240}
241
242impl Default for EventHub {
243 fn default() -> Self {
244 Self::new()
245 }
246}
247
248impl EventHub {
249 pub fn new() -> Self {
251 let (sender, receiver) = unbounded();
252 EventHub {
253 sender,
254 receiver,
255 queue: Arc::new(Mutex::new(Vec::new())),
256 }
257 }
258
259 pub fn start_event_loop(&self, stop_signal: Arc<AtomicBool>) {
261 let receiver = self.receiver.clone();
262 let queue = self.queue.clone();
263 thread::spawn(move || {
264 loop {
265 if stop_signal.load(std::sync::atomic::Ordering::Relaxed) {
266 break;
267 }
268
269 match receiver.recv() {
270 Ok(event) => {
271 let mut queue = queue.lock().unwrap();
272 queue.push(event.clone());
273 }
274 Err(_) => {
275 break;
277 }
278 };
279 }
280 });
281 }
282
283 pub fn send_event(&self, event: Event) {
285 if let Err(e) = self.sender.send(event) {
286 eprintln!("EventHub: failed to send event (receiver dropped): {e}");
287 }
288 }
289
290 pub fn get_queue(&self) -> Queue {
291 self.queue.clone()
292 }
293
294 pub fn subscribe_receiver(&self) -> Receiver<Event> {
301 self.receiver.clone()
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308
309 #[test]
310 fn test_event_hub_send_and_receive() {
311 let event_hub = EventHub::new();
312 let stop_signal = Arc::new(AtomicBool::new(false));
313 event_hub.start_event_loop(stop_signal.clone());
314
315 let event = Event {
316 origin: Origin::DirectAccess(DirectAccessEntity::All(AllEvent::Reset)),
317 ids: vec![EntityId::default()],
318 data: Some("test_data".to_string()),
319 };
320
321 event_hub.send_event(event.clone());
322
323 thread::sleep(std::time::Duration::from_millis(100));
324
325 let queue = event_hub.get_queue();
326 let queue = queue.lock().unwrap();
327 assert_eq!(queue.len(), 1);
328 assert_eq!(queue[0], event);
329
330 stop_signal.store(true, std::sync::atomic::Ordering::Relaxed);
331 }
332}