Skip to main content

text_document_common/
event.rs

1// Generated by Qleany v1.5.1 from common_event.tera
2
3use 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    InsertFragment,
67    InsertHtmlAtPosition,
68    InsertMarkdownAtPosition,
69    InsertTable,
70    RemoveTable,
71    InsertTableRow,
72    InsertTableColumn,
73    RemoveTableRow,
74    RemoveTableColumn,
75    MergeTableCells,
76    SplitTableCell,
77}
78
79#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
80pub enum DocumentFormattingEvent {
81    SetTextFormat,
82    MergeTextFormat,
83    SetBlockFormat,
84    SetFrameFormat,
85    SetTableFormat,
86    SetTableCellFormat,
87}
88
89#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
90pub enum DocumentIoEvent {
91    ImportPlainText,
92    ExportPlainText,
93    ImportMarkdown,
94    ExportMarkdown,
95    ImportHtml,
96    ExportHtml,
97    ExportLatex,
98    ExportDocx,
99}
100
101#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
102pub enum DocumentSearchEvent {
103    FindText,
104    FindAll,
105    ReplaceText,
106}
107
108#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
109pub enum DocumentInspectionEvent {
110    GetDocumentStats,
111    GetTextAtPosition,
112    GetBlockAtPosition,
113    ExtractFragment,
114}
115
116#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
117pub enum Origin {
118    DirectAccess(DirectAccessEntity),
119    UndoRedo(UndoRedoEvent),
120    LongOperation(LongOperationEvent),
121
122    DocumentEditing(DocumentEditingEvent),
123    DocumentFormatting(DocumentFormattingEvent),
124    DocumentIo(DocumentIoEvent),
125    DocumentSearch(DocumentSearchEvent),
126    DocumentInspection(DocumentInspectionEvent),
127}
128
129#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
130pub struct Event {
131    pub origin: Origin,
132    pub ids: Vec<EntityId>,
133    pub data: Option<String>,
134}
135
136impl Event {
137    pub fn origin_string(&self) -> String {
138        match &self.origin {
139            Origin::DirectAccess(entity) => match entity {
140                DirectAccessEntity::All(event) => format!("direct_access_all_{:?}", event),
141                // entities
142                DirectAccessEntity::Root(event) => format!("direct_access_root_{:?}", event),
143                DirectAccessEntity::Document(event) => {
144                    format!("direct_access_document_{:?}", event)
145                }
146                DirectAccessEntity::Frame(event) => format!("direct_access_frame_{:?}", event),
147                DirectAccessEntity::Block(event) => format!("direct_access_block_{:?}", event),
148                DirectAccessEntity::InlineElement(event) => {
149                    format!("direct_access_inline_element_{:?}", event)
150                }
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            // features
163            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}
172/// Thread-safe event buffer for deferring event emissions during transactions.
173///
174/// Repositories push events into this buffer instead of sending them directly
175/// to the EventHub. On commit(), the UoW drains the buffer and sends all events.
176/// On rollback(), the buffer is discarded. This prevents the UI from seeing
177/// phantom state from failed transactions.
178///
179/// This is the Rust equivalent of SignalBuffer in the C++/Qt target.
180pub 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    /// Start buffering. Clears any stale events from a previous cycle.
194    pub fn begin_buffering(&mut self) {
195        self.buffering = true;
196        self.pending.clear();
197    }
198
199    /// Queue an event for deferred delivery.
200    ///
201    /// If buffering is not active, the event is silently dropped.
202    /// (Callers should only push during an active transaction.)
203    pub fn push(&mut self, event: Event) {
204        if self.buffering {
205            self.pending.push(event);
206        }
207    }
208
209    /// Drain all pending events and stop buffering.
210    /// The caller is responsible for sending them to the EventHub.
211    pub fn flush(&mut self) -> Vec<Event> {
212        self.buffering = false;
213        std::mem::take(&mut self.pending)
214    }
215
216    /// Discard all pending events and stop buffering.
217    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/// Central event hub for managing subscriptions and dispatching events
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    /// Create a new event hub
250    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    /// Start the event processing loop
260    pub fn start_event_loop(&self, stop_signal: Arc<AtomicBool>) {
261        let receiver = self.receiver.clone();
262        let queue = self.queue.clone();
263        thread::spawn(move || {
264            loop {
265                if stop_signal.load(std::sync::atomic::Ordering::Relaxed) {
266                    break;
267                }
268
269                match receiver.recv() {
270                    Ok(event) => {
271                        let mut queue = queue.lock().unwrap();
272                        queue.push(event.clone());
273                    }
274                    Err(_) => {
275                        //println!("EventHub receiver dropped");
276                        break;
277                    }
278                };
279            }
280        });
281    }
282
283    /// Send an event to the queue
284    pub fn send_event(&self, event: Event) {
285        if let Err(e) = self.sender.send(event) {
286            eprintln!("EventHub: failed to send event (receiver dropped): {e}");
287        }
288    }
289
290    pub fn get_queue(&self) -> Queue {
291        self.queue.clone()
292    }
293
294    /// Get a direct event receiver.
295    ///
296    /// The receiver blocks on `recv()` until an event arrives — no polling needed.
297    /// **Important**: flume uses MPMC semantics — each event is delivered to exactly
298    /// one receiver. Multiple cloned receivers compete for events rather than each
299    /// receiving a copy. Ensure only one consumer calls `subscribe_receiver()`.
300    pub fn subscribe_receiver(&self) -> Receiver<Event> {
301        self.receiver.clone()
302    }
303}
304
305#[cfg(test)]
306mod tests {
307    use super::*;
308
309    #[test]
310    fn test_event_hub_send_and_receive() {
311        let event_hub = EventHub::new();
312        let stop_signal = Arc::new(AtomicBool::new(false));
313        event_hub.start_event_loop(stop_signal.clone());
314
315        let event = Event {
316            origin: Origin::DirectAccess(DirectAccessEntity::All(AllEvent::Reset)),
317            ids: vec![EntityId::default()],
318            data: Some("test_data".to_string()),
319        };
320
321        event_hub.send_event(event.clone());
322
323        thread::sleep(std::time::Duration::from_millis(100));
324
325        let queue = event_hub.get_queue();
326        let queue = queue.lock().unwrap();
327        assert_eq!(queue.len(), 1);
328        assert_eq!(queue[0], event);
329
330        stop_signal.store(true, std::sync::atomic::Ordering::Relaxed);
331    }
332}