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 InsertDjotAtPosition,
69 InsertTable,
70 RemoveTable,
71 InsertTableRow,
72 InsertTableColumn,
73 RemoveTableRow,
74 RemoveTableColumn,
75 MergeTableCells,
76 SplitTableCell,
77 WrapBlocksInFrame,
78 UnwrapFrame,
79 UnwrapBlockFromFrame,
80}
81
82#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
83pub enum DocumentFormattingEvent {
84 SetTextFormat,
85 MergeTextFormat,
86 SetBlockFormat,
87 SetFrameFormat,
88 SetTableFormat,
89 SetTableCellFormat,
90 SetListFormat,
91}
92
93#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
94pub enum DocumentIoEvent {
95 ImportPlainText,
96 ExportPlainText,
97 ImportMarkdown,
98 ExportMarkdown,
99 ImportHtml,
100 ExportHtml,
101 ImportDjot,
102 ExportDjot,
103 ExportLatex,
104 ExportDocx,
105}
106
107#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
108pub enum DocumentSearchEvent {
109 FindText,
110 FindAll,
111 ReplaceText,
112 ReplaceRanges,
113 AddressableText,
114}
115
116#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
117pub enum DocumentInspectionEvent {
118 GetDocumentStats,
119 GetTextAtPosition,
120 GetBlockAtPosition,
121 ExtractFragment,
122}
123
124#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
125pub enum Origin {
126 DirectAccess(DirectAccessEntity),
127 UndoRedo(UndoRedoEvent),
128 LongOperation(LongOperationEvent),
129
130 DocumentEditing(DocumentEditingEvent),
131 DocumentFormatting(DocumentFormattingEvent),
132 DocumentIo(DocumentIoEvent),
133 DocumentSearch(DocumentSearchEvent),
134 DocumentInspection(DocumentInspectionEvent),
135}
136
137#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
138pub struct Event {
139 pub origin: Origin,
140 pub ids: Vec<EntityId>,
141 pub data: Option<String>,
142}
143
144impl Event {
145 pub fn origin_string(&self) -> String {
146 match &self.origin {
147 Origin::DirectAccess(entity) => match entity {
148 DirectAccessEntity::All(event) => format!("direct_access_all_{:?}", event),
149 DirectAccessEntity::Root(event) => format!("direct_access_root_{:?}", event),
151 DirectAccessEntity::Document(event) => {
152 format!("direct_access_document_{:?}", event)
153 }
154 DirectAccessEntity::Frame(event) => format!("direct_access_frame_{:?}", event),
155 DirectAccessEntity::Block(event) => format!("direct_access_block_{:?}", event),
156 DirectAccessEntity::List(event) => format!("direct_access_list_{:?}", event),
157 DirectAccessEntity::Resource(event) => {
158 format!("direct_access_resource_{:?}", event)
159 }
160 DirectAccessEntity::Table(event) => format!("direct_access_table_{:?}", event),
161 DirectAccessEntity::TableCell(event) => {
162 format!("direct_access_table_cell_{:?}", event)
163 }
164 },
165 Origin::UndoRedo(event) => format!("undo_redo_{:?}", event),
166 Origin::LongOperation(event) => format!("long_operation_{:?}", event),
167 Origin::DocumentEditing(event) => format!("document_editing_{:?}", event),
169 Origin::DocumentFormatting(event) => format!("document_formatting_{:?}", event),
170 Origin::DocumentIo(event) => format!("document_io_{:?}", event),
171 Origin::DocumentSearch(event) => format!("document_search_{:?}", event),
172 Origin::DocumentInspection(event) => format!("document_inspection_{:?}", event),
173 }
174 .to_lowercase()
175 }
176}
177pub struct EventBuffer {
186 buffering: bool,
187 pending: Vec<Event>,
188}
189
190impl EventBuffer {
191 pub fn new() -> Self {
192 Self {
193 buffering: false,
194 pending: Vec::new(),
195 }
196 }
197
198 pub fn begin_buffering(&mut self) {
200 self.buffering = true;
201 self.pending.clear();
202 }
203
204 pub fn push(&mut self, event: Event) {
209 if self.buffering {
210 self.pending.push(event);
211 }
212 }
213
214 pub fn flush(&mut self) -> Vec<Event> {
217 self.buffering = false;
218 std::mem::take(&mut self.pending)
219 }
220
221 pub fn discard(&mut self) {
223 self.buffering = false;
224 self.pending.clear();
225 }
226
227 pub fn is_buffering(&self) -> bool {
228 self.buffering
229 }
230}
231
232impl Default for EventBuffer {
233 fn default() -> Self {
234 Self::new()
235 }
236}
237
238pub type Queue = Arc<Mutex<Vec<Event>>>;
239
240#[derive(Debug)]
242pub struct EventHub {
243 sender: Sender<Event>,
244 receiver: Receiver<Event>,
245 queue: Queue,
246}
247
248impl Default for EventHub {
249 fn default() -> Self {
250 Self::new()
251 }
252}
253
254impl EventHub {
255 pub fn new() -> Self {
257 let (sender, receiver) = unbounded();
258 EventHub {
259 sender,
260 receiver,
261 queue: Arc::new(Mutex::new(Vec::new())),
262 }
263 }
264
265 pub fn start_event_loop(&self, shutdown_rx: Receiver<()>) -> thread::JoinHandle<()> {
273 let receiver = self.receiver.clone();
274 let queue = self.queue.clone();
275 thread::spawn(move || {
276 loop {
277 let outcome: Result<Option<Event>, ()> = flume::Selector::new()
278 .recv(&receiver, |r| r.map(Some).map_err(|_| ()))
279 .recv(&shutdown_rx, |_| Ok(None))
280 .wait();
281 match outcome {
282 Ok(Some(event)) => {
283 let mut queue = queue.lock();
284 queue.push(event);
285 }
286 Ok(None) | Err(()) => break,
287 }
288 }
289 })
290 }
291
292 pub fn send_event(&self, event: Event) {
294 if let Err(e) = self.sender.send(event) {
295 eprintln!("EventHub: failed to send event (receiver dropped): {e}");
296 }
297 }
298
299 pub fn get_queue(&self) -> Queue {
300 self.queue.clone()
301 }
302
303 pub fn subscribe_receiver(&self) -> Receiver<Event> {
310 self.receiver.clone()
311 }
312}
313
314#[cfg(test)]
315mod tests {
316 use super::*;
317
318 #[test]
319 fn test_event_hub_send_and_receive() {
320 let event_hub = EventHub::new();
321 let (shutdown_tx, shutdown_rx) = flume::bounded::<()>(1);
322 let handle = event_hub.start_event_loop(shutdown_rx);
323
324 let event = Event {
325 origin: Origin::DirectAccess(DirectAccessEntity::All(AllEvent::Reset)),
326 ids: vec![EntityId::default()],
327 data: Some("test_data".to_string()),
328 };
329
330 event_hub.send_event(event.clone());
331
332 thread::sleep(std::time::Duration::from_millis(100));
333
334 let queue = event_hub.get_queue();
335 let queue = queue.lock();
336 assert_eq!(queue.len(), 1);
337 assert_eq!(queue[0], event);
338
339 drop(shutdown_tx);
341 handle.join().unwrap();
342 }
343}