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 AddBlockToList,
67 RemoveBlockFromList,
68 InsertFragment,
69 InsertHtmlAtPosition,
70 InsertMarkdownAtPosition,
71 InsertTable,
72 RemoveTable,
73 InsertTableRow,
74 InsertTableColumn,
75 RemoveTableRow,
76 RemoveTableColumn,
77 MergeTableCells,
78 SplitTableCell,
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::InlineElement(event) => {
152 format!("direct_access_inline_element_{:?}", event)
153 }
154 DirectAccessEntity::List(event) => format!("direct_access_list_{:?}", event),
155 DirectAccessEntity::Resource(event) => {
156 format!("direct_access_resource_{:?}", event)
157 }
158 DirectAccessEntity::Table(event) => format!("direct_access_table_{:?}", event),
159 DirectAccessEntity::TableCell(event) => {
160 format!("direct_access_table_cell_{:?}", event)
161 }
162 },
163 Origin::UndoRedo(event) => format!("undo_redo_{:?}", event),
164 Origin::LongOperation(event) => format!("long_operation_{:?}", event),
165 Origin::DocumentEditing(event) => format!("document_editing_{:?}", event),
167 Origin::DocumentFormatting(event) => format!("document_formatting_{:?}", event),
168 Origin::DocumentIo(event) => format!("document_io_{:?}", event),
169 Origin::DocumentSearch(event) => format!("document_search_{:?}", event),
170 Origin::DocumentInspection(event) => format!("document_inspection_{:?}", event),
171 }
172 .to_lowercase()
173 }
174}
175pub struct EventBuffer {
184 buffering: bool,
185 pending: Vec<Event>,
186}
187
188impl EventBuffer {
189 pub fn new() -> Self {
190 Self {
191 buffering: false,
192 pending: Vec::new(),
193 }
194 }
195
196 pub fn begin_buffering(&mut self) {
198 self.buffering = true;
199 self.pending.clear();
200 }
201
202 pub fn push(&mut self, event: Event) {
207 if self.buffering {
208 self.pending.push(event);
209 }
210 }
211
212 pub fn flush(&mut self) -> Vec<Event> {
215 self.buffering = false;
216 std::mem::take(&mut self.pending)
217 }
218
219 pub fn discard(&mut self) {
221 self.buffering = false;
222 self.pending.clear();
223 }
224
225 pub fn is_buffering(&self) -> bool {
226 self.buffering
227 }
228}
229
230impl Default for EventBuffer {
231 fn default() -> Self {
232 Self::new()
233 }
234}
235
236pub type Queue = Arc<Mutex<Vec<Event>>>;
237
238#[derive(Debug)]
240pub struct EventHub {
241 sender: Sender<Event>,
242 receiver: Receiver<Event>,
243 queue: Queue,
244}
245
246impl Default for EventHub {
247 fn default() -> Self {
248 Self::new()
249 }
250}
251
252impl EventHub {
253 pub fn new() -> Self {
255 let (sender, receiver) = unbounded();
256 EventHub {
257 sender,
258 receiver,
259 queue: Arc::new(Mutex::new(Vec::new())),
260 }
261 }
262
263 pub fn start_event_loop(&self, stop_signal: Arc<AtomicBool>) -> thread::JoinHandle<()> {
269 let receiver = self.receiver.clone();
270 let queue = self.queue.clone();
271 thread::spawn(move || {
272 loop {
273 if stop_signal.load(std::sync::atomic::Ordering::Relaxed) {
274 break;
275 }
276
277 match receiver.recv_timeout(std::time::Duration::from_millis(100)) {
278 Ok(event) => {
279 let mut queue = queue.lock().unwrap();
280 queue.push(event.clone());
281 }
282 Err(flume::RecvTimeoutError::Timeout) => {
283 continue;
285 }
286 Err(flume::RecvTimeoutError::Disconnected) => {
287 break;
288 }
289 };
290 }
291 })
292 }
293
294 pub fn send_event(&self, event: Event) {
296 if let Err(e) = self.sender.send(event) {
297 eprintln!("EventHub: failed to send event (receiver dropped): {e}");
298 }
299 }
300
301 pub fn get_queue(&self) -> Queue {
302 self.queue.clone()
303 }
304
305 pub fn subscribe_receiver(&self) -> Receiver<Event> {
312 self.receiver.clone()
313 }
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319
320 #[test]
321 fn test_event_hub_send_and_receive() {
322 let event_hub = EventHub::new();
323 let stop_signal = Arc::new(AtomicBool::new(false));
324 let _handle = event_hub.start_event_loop(stop_signal.clone());
325
326 let event = Event {
327 origin: Origin::DirectAccess(DirectAccessEntity::All(AllEvent::Reset)),
328 ids: vec![EntityId::default()],
329 data: Some("test_data".to_string()),
330 };
331
332 event_hub.send_event(event.clone());
333
334 thread::sleep(std::time::Duration::from_millis(100));
335
336 let queue = event_hub.get_queue();
337 let queue = queue.lock().unwrap();
338 assert_eq!(queue.len(), 1);
339 assert_eq!(queue[0], event);
340
341 stop_signal.store(true, std::sync::atomic::Ordering::Relaxed);
342 }
343}