1use crate::types::EntityId;
4use flume::{Receiver, Sender, unbounded};
5use serde::Serialize;
6use std::{
7 sync::{Arc, Mutex},
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 List(EntityEvent),
50 Resource(EntityEvent),
51 Table(EntityEvent),
52 TableCell(EntityEvent),
53}
54
55#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
56pub enum DocumentEditingEvent {
57 InsertText,
58 DeleteText,
59 InsertBlock,
60 InsertImage,
61 InsertFrame,
62 InsertFormattedText,
63 CreateList,
64 InsertList,
65 AddBlockToList,
66 RemoveBlockFromList,
67 InsertFragment,
68 InsertHtmlAtPosition,
69 InsertMarkdownAtPosition,
70 InsertTable,
71 RemoveTable,
72 InsertTableRow,
73 InsertTableColumn,
74 RemoveTableRow,
75 RemoveTableColumn,
76 MergeTableCells,
77 SplitTableCell,
78}
79
80#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
81pub enum DocumentFormattingEvent {
82 SetTextFormat,
83 MergeTextFormat,
84 SetBlockFormat,
85 SetFrameFormat,
86 SetTableFormat,
87 SetTableCellFormat,
88 SetListFormat,
89}
90
91#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
92pub enum DocumentIoEvent {
93 ImportPlainText,
94 ExportPlainText,
95 ImportMarkdown,
96 ExportMarkdown,
97 ImportHtml,
98 ExportHtml,
99 ExportLatex,
100 ExportDocx,
101}
102
103#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
104pub enum DocumentSearchEvent {
105 FindText,
106 FindAll,
107 ReplaceText,
108}
109
110#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
111pub enum DocumentInspectionEvent {
112 GetDocumentStats,
113 GetTextAtPosition,
114 GetBlockAtPosition,
115 ExtractFragment,
116}
117
118#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
119pub enum Origin {
120 DirectAccess(DirectAccessEntity),
121 UndoRedo(UndoRedoEvent),
122 LongOperation(LongOperationEvent),
123
124 DocumentEditing(DocumentEditingEvent),
125 DocumentFormatting(DocumentFormattingEvent),
126 DocumentIo(DocumentIoEvent),
127 DocumentSearch(DocumentSearchEvent),
128 DocumentInspection(DocumentInspectionEvent),
129}
130
131#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
132pub struct Event {
133 pub origin: Origin,
134 pub ids: Vec<EntityId>,
135 pub data: Option<String>,
136}
137
138impl Event {
139 pub fn origin_string(&self) -> String {
140 match &self.origin {
141 Origin::DirectAccess(entity) => match entity {
142 DirectAccessEntity::All(event) => format!("direct_access_all_{:?}", event),
143 DirectAccessEntity::Root(event) => format!("direct_access_root_{:?}", event),
145 DirectAccessEntity::Document(event) => {
146 format!("direct_access_document_{:?}", event)
147 }
148 DirectAccessEntity::Frame(event) => format!("direct_access_frame_{:?}", event),
149 DirectAccessEntity::Block(event) => format!("direct_access_block_{:?}", event),
150 DirectAccessEntity::List(event) => format!("direct_access_list_{:?}", event),
151 DirectAccessEntity::Resource(event) => {
152 format!("direct_access_resource_{:?}", event)
153 }
154 DirectAccessEntity::Table(event) => format!("direct_access_table_{:?}", event),
155 DirectAccessEntity::TableCell(event) => {
156 format!("direct_access_table_cell_{:?}", event)
157 }
158 },
159 Origin::UndoRedo(event) => format!("undo_redo_{:?}", event),
160 Origin::LongOperation(event) => format!("long_operation_{:?}", event),
161 Origin::DocumentEditing(event) => format!("document_editing_{:?}", event),
163 Origin::DocumentFormatting(event) => format!("document_formatting_{:?}", event),
164 Origin::DocumentIo(event) => format!("document_io_{:?}", event),
165 Origin::DocumentSearch(event) => format!("document_search_{:?}", event),
166 Origin::DocumentInspection(event) => format!("document_inspection_{:?}", event),
167 }
168 .to_lowercase()
169 }
170}
171pub struct EventBuffer {
180 buffering: bool,
181 pending: Vec<Event>,
182}
183
184impl EventBuffer {
185 pub fn new() -> Self {
186 Self {
187 buffering: false,
188 pending: Vec::new(),
189 }
190 }
191
192 pub fn begin_buffering(&mut self) {
194 self.buffering = true;
195 self.pending.clear();
196 }
197
198 pub fn push(&mut self, event: Event) {
203 if self.buffering {
204 self.pending.push(event);
205 }
206 }
207
208 pub fn flush(&mut self) -> Vec<Event> {
211 self.buffering = false;
212 std::mem::take(&mut self.pending)
213 }
214
215 pub fn discard(&mut self) {
217 self.buffering = false;
218 self.pending.clear();
219 }
220
221 pub fn is_buffering(&self) -> bool {
222 self.buffering
223 }
224}
225
226impl Default for EventBuffer {
227 fn default() -> Self {
228 Self::new()
229 }
230}
231
232pub type Queue = Arc<Mutex<Vec<Event>>>;
233
234#[derive(Debug)]
236pub 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, shutdown_rx: Receiver<()>) -> thread::JoinHandle<()> {
267 let receiver = self.receiver.clone();
268 let queue = self.queue.clone();
269 thread::spawn(move || {
270 loop {
271 let outcome: Result<Option<Event>, ()> = flume::Selector::new()
272 .recv(&receiver, |r| r.map(Some).map_err(|_| ()))
273 .recv(&shutdown_rx, |_| Ok(None))
274 .wait();
275 match outcome {
276 Ok(Some(event)) => {
277 let mut queue = queue.lock().unwrap();
278 queue.push(event);
279 }
280 Ok(None) | Err(()) => break,
281 }
282 }
283 })
284 }
285
286 pub fn send_event(&self, event: Event) {
288 if let Err(e) = self.sender.send(event) {
289 eprintln!("EventHub: failed to send event (receiver dropped): {e}");
290 }
291 }
292
293 pub fn get_queue(&self) -> Queue {
294 self.queue.clone()
295 }
296
297 pub fn subscribe_receiver(&self) -> Receiver<Event> {
304 self.receiver.clone()
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use super::*;
311
312 #[test]
313 fn test_event_hub_send_and_receive() {
314 let event_hub = EventHub::new();
315 let (shutdown_tx, shutdown_rx) = flume::bounded::<()>(1);
316 let handle = event_hub.start_event_loop(shutdown_rx);
317
318 let event = Event {
319 origin: Origin::DirectAccess(DirectAccessEntity::All(AllEvent::Reset)),
320 ids: vec![EntityId::default()],
321 data: Some("test_data".to_string()),
322 };
323
324 event_hub.send_event(event.clone());
325
326 thread::sleep(std::time::Duration::from_millis(100));
327
328 let queue = event_hub.get_queue();
329 let queue = queue.lock().unwrap();
330 assert_eq!(queue.len(), 1);
331 assert_eq!(queue[0], event);
332
333 drop(shutdown_tx);
335 handle.join().unwrap();
336 }
337}