Skip to main content

common/
event.rs

1// Generated by Qleany v1.7.3 from common_event.tera
2
3use 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                // entities
150                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            // features
168            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}
177/// Thread-safe event buffer for deferring event emissions during transactions.
178///
179/// Repositories push events into this buffer instead of sending them directly
180/// to the EventHub. On commit(), the UoW drains the buffer and sends all events.
181/// On rollback(), the buffer is discarded. This prevents the UI from seeing
182/// phantom state from failed transactions.
183///
184/// This is the Rust equivalent of SignalBuffer in the C++/Qt target.
185pub 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    /// Start buffering. Clears any stale events from a previous cycle.
199    pub fn begin_buffering(&mut self) {
200        self.buffering = true;
201        self.pending.clear();
202    }
203
204    /// Queue an event for deferred delivery.
205    ///
206    /// If buffering is not active, the event is silently dropped.
207    /// (Callers should only push during an active transaction.)
208    pub fn push(&mut self, event: Event) {
209        if self.buffering {
210            self.pending.push(event);
211        }
212    }
213
214    /// Drain all pending events and stop buffering.
215    /// The caller is responsible for sending them to the EventHub.
216    pub fn flush(&mut self) -> Vec<Event> {
217        self.buffering = false;
218        std::mem::take(&mut self.pending)
219    }
220
221    /// Discard all pending events and stop buffering.
222    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/// Central event hub for managing subscriptions and dispatching events
241#[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    /// Create a new event hub
256    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    /// Start the event processing loop.
266    ///
267    /// Returns a `JoinHandle` so the caller can join the thread on
268    /// shutdown. The thread blocks on a `flume::Selector` that wakes
269    /// only when an event arrives or `shutdown_rx` disconnects — zero
270    /// CPU while idle. Drop the matching `Sender<()>` to stop the
271    /// thread.
272    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    /// Send an event to the queue
293    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    /// Get a direct event receiver.
304    ///
305    /// The receiver blocks on `recv()` until an event arrives — no polling needed.
306    /// **Important**: flume uses MPMC semantics — each event is delivered to exactly
307    /// one receiver. Multiple cloned receivers compete for events rather than each
308    /// receiving a copy. Ensure only one consumer calls `subscribe_receiver()`.
309    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 the sender to wake the loop thread and exit cleanly.
340        drop(shutdown_tx);
341        handle.join().unwrap();
342    }
343}