1use crate::types::EntityId;
4use flume::{Receiver, Sender, unbounded};
5use parking_lot::Mutex;
6use serde::Serialize;
7use std::{sync::Arc, thread};
8
9#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
10pub enum EntityEvent {
11 Created,
12 Updated,
13 Removed,
14}
15
16#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
17pub enum AllEvent {
18 Reset,
19}
20
21#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
22pub enum UndoRedoEvent {
23 Undone,
24 Redone,
25 BeginComposite,
26 EndComposite,
27 CancelComposite,
28}
29
30#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
31pub enum LongOperationEvent {
32 Started,
33 Progress,
34 Cancelled,
35 Completed,
36 Failed,
37}
38
39#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
40pub enum DirectAccessEntity {
41 All(AllEvent),
42
43 Root(EntityEvent),
44 Document(EntityEvent),
45 Frame(EntityEvent),
46 Block(EntityEvent),
47 List(EntityEvent),
48 Resource(EntityEvent),
49 Table(EntityEvent),
50 TableCell(EntityEvent),
51}
52
53#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
54pub enum DocumentEditingEvent {
55 InsertText,
56 DeleteText,
57 InsertBlock,
58 InsertImage,
59 InsertFrame,
60 InsertFormattedText,
61 CreateList,
62 InsertList,
63 AddBlockToList,
64 RemoveBlockFromList,
65 InsertFragment,
66 InsertHtmlAtPosition,
67 InsertMarkdownAtPosition,
68 InsertTable,
69 RemoveTable,
70 InsertTableRow,
71 InsertTableColumn,
72 RemoveTableRow,
73 RemoveTableColumn,
74 MergeTableCells,
75 SplitTableCell,
76 WrapBlocksInFrame,
77 UnwrapFrame,
78 UnwrapBlockFromFrame,
79}
80
81#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
82pub enum DocumentFormattingEvent {
83 SetTextFormat,
84 MergeTextFormat,
85 SetBlockFormat,
86 SetFrameFormat,
87 SetTableFormat,
88 SetTableCellFormat,
89 SetListFormat,
90}
91
92#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
93pub enum DocumentIoEvent {
94 ImportPlainText,
95 ExportPlainText,
96 ImportMarkdown,
97 ExportMarkdown,
98 ImportHtml,
99 ExportHtml,
100 ExportLatex,
101 ExportDocx,
102}
103
104#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
105pub enum DocumentSearchEvent {
106 FindText,
107 FindAll,
108 ReplaceText,
109}
110
111#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
112pub enum DocumentInspectionEvent {
113 GetDocumentStats,
114 GetTextAtPosition,
115 GetBlockAtPosition,
116 ExtractFragment,
117}
118
119#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
120pub enum Origin {
121 DirectAccess(DirectAccessEntity),
122 UndoRedo(UndoRedoEvent),
123 LongOperation(LongOperationEvent),
124
125 DocumentEditing(DocumentEditingEvent),
126 DocumentFormatting(DocumentFormattingEvent),
127 DocumentIo(DocumentIoEvent),
128 DocumentSearch(DocumentSearchEvent),
129 DocumentInspection(DocumentInspectionEvent),
130}
131
132#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
133pub struct Event {
134 pub origin: Origin,
135 pub ids: Vec<EntityId>,
136 pub data: Option<String>,
137}
138
139impl Event {
140 pub fn origin_string(&self) -> String {
141 match &self.origin {
142 Origin::DirectAccess(entity) => match entity {
143 DirectAccessEntity::All(event) => format!("direct_access_all_{:?}", event),
144 DirectAccessEntity::Root(event) => format!("direct_access_root_{:?}", event),
146 DirectAccessEntity::Document(event) => {
147 format!("direct_access_document_{:?}", event)
148 }
149 DirectAccessEntity::Frame(event) => format!("direct_access_frame_{:?}", event),
150 DirectAccessEntity::Block(event) => format!("direct_access_block_{:?}", event),
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
235#[derive(Debug)]
237pub struct EventHub {
238 sender: Sender<Event>,
239 receiver: Receiver<Event>,
240 queue: Queue,
241}
242
243impl Default for EventHub {
244 fn default() -> Self {
245 Self::new()
246 }
247}
248
249impl EventHub {
250 pub fn new() -> Self {
252 let (sender, receiver) = unbounded();
253 EventHub {
254 sender,
255 receiver,
256 queue: Arc::new(Mutex::new(Vec::new())),
257 }
258 }
259
260 pub fn start_event_loop(&self, shutdown_rx: Receiver<()>) -> thread::JoinHandle<()> {
268 let receiver = self.receiver.clone();
269 let queue = self.queue.clone();
270 thread::spawn(move || {
271 loop {
272 let outcome: Result<Option<Event>, ()> = flume::Selector::new()
273 .recv(&receiver, |r| r.map(Some).map_err(|_| ()))
274 .recv(&shutdown_rx, |_| Ok(None))
275 .wait();
276 match outcome {
277 Ok(Some(event)) => {
278 let mut queue = queue.lock();
279 queue.push(event);
280 }
281 Ok(None) | Err(()) => break,
282 }
283 }
284 })
285 }
286
287 pub fn send_event(&self, event: Event) {
289 if let Err(e) = self.sender.send(event) {
290 eprintln!("EventHub: failed to send event (receiver dropped): {e}");
291 }
292 }
293
294 pub fn get_queue(&self) -> Queue {
295 self.queue.clone()
296 }
297
298 pub fn subscribe_receiver(&self) -> Receiver<Event> {
305 self.receiver.clone()
306 }
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312
313 #[test]
314 fn test_event_hub_send_and_receive() {
315 let event_hub = EventHub::new();
316 let (shutdown_tx, shutdown_rx) = flume::bounded::<()>(1);
317 let handle = event_hub.start_event_loop(shutdown_rx);
318
319 let event = Event {
320 origin: Origin::DirectAccess(DirectAccessEntity::All(AllEvent::Reset)),
321 ids: vec![EntityId::default()],
322 data: Some("test_data".to_string()),
323 };
324
325 event_hub.send_event(event.clone());
326
327 thread::sleep(std::time::Duration::from_millis(100));
328
329 let queue = event_hub.get_queue();
330 let queue = queue.lock();
331 assert_eq!(queue.len(), 1);
332 assert_eq!(queue[0], event);
333
334 drop(shutdown_tx);
336 handle.join().unwrap();
337 }
338}