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 StackChanged,
34 BeginComposite,
35 EndComposite,
36 CancelComposite,
37}
38
39#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
40pub enum LongOperationEvent {
41 Started,
42 Progress,
43 Cancelled,
44 Completed,
45 Failed,
46}
47
48#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
49pub enum DirectAccessEntity {
50 All(AllEvent),
51
52 Root(EntityEvent),
53 Document(EntityEvent),
54 Frame(EntityEvent),
55 Block(EntityEvent),
56 List(EntityEvent),
57 Resource(EntityEvent),
58 Table(EntityEvent),
59 TableCell(EntityEvent),
60}
61
62#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
63pub enum DocumentEditingEvent {
64 InsertText,
65 DeleteText,
66 InsertBlock,
67 InsertImage,
68 InsertFrame,
69 InsertFormattedText,
70 CreateList,
71 InsertList,
72 AddBlockToList,
73 RemoveBlockFromList,
74 InsertFragment,
75 InsertHtmlAtPosition,
76 InsertMarkdownAtPosition,
77 InsertDjotAtPosition,
78 InsertTable,
79 RemoveTable,
80 InsertTableRow,
81 InsertTableColumn,
82 RemoveTableRow,
83 RemoveTableColumn,
84 MergeTableCells,
85 SplitTableCell,
86 WrapBlocksInFrame,
87 UnwrapFrame,
88 UnwrapBlockFromFrame,
89}
90
91#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
92pub enum DocumentFormattingEvent {
93 SetTextFormat,
94 MergeTextFormat,
95 SetBlockFormat,
96 SetFrameFormat,
97 SetTableFormat,
98 SetTableCellFormat,
99 SetListFormat,
100}
101
102#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
103pub enum DocumentIoEvent {
104 ImportPlainText,
105 ExportPlainText,
106 ImportMarkdown,
107 ExportMarkdown,
108 ImportHtml,
109 ExportHtml,
110 ImportDjot,
111 ExportDjot,
112 ExportLatex,
113 ExportDocx,
114}
115
116#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
117pub enum DocumentSearchEvent {
118 FindText,
119 FindAll,
120 ReplaceText,
121 ReplaceRanges,
122 AddressableText,
123}
124
125#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
126pub enum DocumentInspectionEvent {
127 GetDocumentStats,
128 GetTextAtPosition,
129 GetBlockAtPosition,
130 ExtractFragment,
131}
132
133#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
134pub enum Origin {
135 DirectAccess(DirectAccessEntity),
136 UndoRedo(UndoRedoEvent),
137 LongOperation(LongOperationEvent),
138
139 DocumentEditing(DocumentEditingEvent),
140 DocumentFormatting(DocumentFormattingEvent),
141 DocumentIo(DocumentIoEvent),
142 DocumentSearch(DocumentSearchEvent),
143 DocumentInspection(DocumentInspectionEvent),
144}
145
146#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
147pub struct Event {
148 pub origin: Origin,
149 pub ids: Vec<EntityId>,
150 pub data: Option<String>,
151}
152
153impl Event {
154 pub fn origin_string(&self) -> String {
155 match &self.origin {
156 Origin::DirectAccess(entity) => match entity {
157 DirectAccessEntity::All(event) => format!("direct_access_all_{:?}", event),
158 DirectAccessEntity::Root(event) => format!("direct_access_root_{:?}", event),
160 DirectAccessEntity::Document(event) => {
161 format!("direct_access_document_{:?}", event)
162 }
163 DirectAccessEntity::Frame(event) => format!("direct_access_frame_{:?}", event),
164 DirectAccessEntity::Block(event) => format!("direct_access_block_{:?}", event),
165 DirectAccessEntity::List(event) => format!("direct_access_list_{:?}", event),
166 DirectAccessEntity::Resource(event) => {
167 format!("direct_access_resource_{:?}", event)
168 }
169 DirectAccessEntity::Table(event) => format!("direct_access_table_{:?}", event),
170 DirectAccessEntity::TableCell(event) => {
171 format!("direct_access_table_cell_{:?}", event)
172 }
173 },
174 Origin::UndoRedo(event) => format!("undo_redo_{:?}", event),
175 Origin::LongOperation(event) => format!("long_operation_{:?}", event),
176 Origin::DocumentEditing(event) => format!("document_editing_{:?}", event),
178 Origin::DocumentFormatting(event) => format!("document_formatting_{:?}", event),
179 Origin::DocumentIo(event) => format!("document_io_{:?}", event),
180 Origin::DocumentSearch(event) => format!("document_search_{:?}", event),
181 Origin::DocumentInspection(event) => format!("document_inspection_{:?}", event),
182 }
183 .to_lowercase()
184 }
185}
186pub struct EventBuffer {
195 buffering: bool,
196 pending: Vec<Event>,
197}
198
199impl EventBuffer {
200 pub fn new() -> Self {
201 Self {
202 buffering: false,
203 pending: Vec::new(),
204 }
205 }
206
207 pub fn begin_buffering(&mut self) {
209 self.buffering = true;
210 self.pending.clear();
211 }
212
213 pub fn push(&mut self, event: Event) {
218 if self.buffering {
219 self.pending.push(event);
220 }
221 }
222
223 pub fn flush(&mut self) -> Vec<Event> {
226 self.buffering = false;
227 std::mem::take(&mut self.pending)
228 }
229
230 pub fn discard(&mut self) {
232 self.buffering = false;
233 self.pending.clear();
234 }
235
236 pub fn is_buffering(&self) -> bool {
237 self.buffering
238 }
239}
240
241impl Default for EventBuffer {
242 fn default() -> Self {
243 Self::new()
244 }
245}
246
247pub type Queue = Arc<Mutex<Vec<Event>>>;
248
249#[derive(Debug)]
251pub struct EventHub {
252 sender: Sender<Event>,
253 receiver: Receiver<Event>,
254 queue: Queue,
255}
256
257impl Default for EventHub {
258 fn default() -> Self {
259 Self::new()
260 }
261}
262
263impl EventHub {
264 pub fn new() -> Self {
266 let (sender, receiver) = unbounded();
267 EventHub {
268 sender,
269 receiver,
270 queue: Arc::new(Mutex::new(Vec::new())),
271 }
272 }
273
274 pub fn start_event_loop(&self, shutdown_rx: Receiver<()>) -> thread::JoinHandle<()> {
282 let receiver = self.receiver.clone();
283 let queue = self.queue.clone();
284 thread::spawn(move || {
285 loop {
286 let outcome: Result<Option<Event>, ()> = flume::Selector::new()
287 .recv(&receiver, |r| r.map(Some).map_err(|_| ()))
288 .recv(&shutdown_rx, |_| Ok(None))
289 .wait();
290 match outcome {
291 Ok(Some(event)) => {
292 let mut queue = queue.lock();
293 queue.push(event);
294 }
295 Ok(None) | Err(()) => break,
296 }
297 }
298 })
299 }
300
301 pub fn send_event(&self, event: Event) {
303 if let Err(e) = self.sender.send(event) {
304 eprintln!("EventHub: failed to send event (receiver dropped): {e}");
305 }
306 }
307
308 pub fn get_queue(&self) -> Queue {
309 self.queue.clone()
310 }
311
312 pub fn subscribe_receiver(&self) -> Receiver<Event> {
319 self.receiver.clone()
320 }
321}
322
323#[cfg(test)]
324mod tests {
325 use super::*;
326
327 #[test]
328 fn test_event_hub_send_and_receive() {
329 let event_hub = EventHub::new();
330 let (shutdown_tx, shutdown_rx) = flume::bounded::<()>(1);
331 let handle = event_hub.start_event_loop(shutdown_rx);
332
333 let event = Event {
334 origin: Origin::DirectAccess(DirectAccessEntity::All(AllEvent::Reset)),
335 ids: vec![EntityId::default()],
336 data: Some("test_data".to_string()),
337 };
338
339 event_hub.send_event(event.clone());
340
341 thread::sleep(std::time::Duration::from_millis(100));
342
343 let queue = event_hub.get_queue();
344 let queue = queue.lock();
345 assert_eq!(queue.len(), 1);
346 assert_eq!(queue[0], event);
347
348 drop(shutdown_tx);
350 handle.join().unwrap();
351 }
352}