Skip to main content

common/
event.rs

1// Generated by Qleany v0.0.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    Workspace(EntityEvent),
47    System(EntityEvent),
48    Entity(EntityEvent),
49    Field(EntityEvent),
50    Feature(EntityEvent),
51    File(EntityEvent),
52    UseCase(EntityEvent),
53    Dto(EntityEvent),
54    DtoField(EntityEvent),
55    Global(EntityEvent),
56    Relationship(EntityEvent),
57    UserInterface(EntityEvent),
58}
59
60#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
61pub enum HandlingAppLifecycleEvent {
62    InitializeApp,
63    CleanUpBeforeExit,
64}
65
66#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
67pub enum HandlingManifestEvent {
68    Load,
69    Save,
70    Create,
71    Close,
72    ExportToMermaid,
73    Check,
74}
75
76#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
77pub enum RustFileGenerationEvent {
78    FillRustFiles,
79    FillCodeInRustFiles,
80    GenerateRustCode,
81    GenerateRustFiles,
82    GenerateRustPrompt,
83}
84
85#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
86pub enum CppQtFileGenerationEvent {
87    FillCppQtFiles,
88    GenerateCppQtCode,
89    GenerateCppQtFiles,
90    FillCodeInCppQtFiles,
91    GenerateCppQtPrompt,
92}
93
94#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
95pub enum FileGenerationSharedStepsEvent {
96    FillStatusInFiles,
97    GetFileDiff,
98}
99
100#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
101pub enum Origin {
102    DirectAccess(DirectAccessEntity),
103    UndoRedo(UndoRedoEvent),
104    LongOperation(LongOperationEvent),
105
106    HandlingAppLifecycle(HandlingAppLifecycleEvent),
107    HandlingManifest(HandlingManifestEvent),
108    RustFileGeneration(RustFileGenerationEvent),
109    CppQtFileGeneration(CppQtFileGenerationEvent),
110    FileGenerationSharedSteps(FileGenerationSharedStepsEvent),
111}
112
113#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
114pub struct Event {
115    pub origin: Origin,
116    pub ids: Vec<EntityId>,
117    pub data: Option<String>,
118}
119
120impl Event {
121    pub fn origin_string(&self) -> String {
122        match &self.origin {
123            Origin::DirectAccess(entity) => match entity {
124                DirectAccessEntity::All(event) => format!("direct_access_all_{:?}", event),
125                // entities
126                DirectAccessEntity::Root(event) => format!("direct_access_root_{:?}", event),
127                DirectAccessEntity::Workspace(event) => {
128                    format!("direct_access_workspace_{:?}", event)
129                }
130                DirectAccessEntity::System(event) => format!("direct_access_system_{:?}", event),
131                DirectAccessEntity::Entity(event) => format!("direct_access_entity_{:?}", event),
132                DirectAccessEntity::Field(event) => format!("direct_access_field_{:?}", event),
133                DirectAccessEntity::Feature(event) => format!("direct_access_feature_{:?}", event),
134                DirectAccessEntity::File(event) => format!("direct_access_file_{:?}", event),
135                DirectAccessEntity::UseCase(event) => format!("direct_access_use_case_{:?}", event),
136                DirectAccessEntity::Dto(event) => format!("direct_access_dto_{:?}", event),
137                DirectAccessEntity::DtoField(event) => {
138                    format!("direct_access_dto_field_{:?}", event)
139                }
140                DirectAccessEntity::Global(event) => format!("direct_access_global_{:?}", event),
141                DirectAccessEntity::Relationship(event) => {
142                    format!("direct_access_relationship_{:?}", event)
143                }
144                DirectAccessEntity::UserInterface(event) => {
145                    format!("direct_access_user_interface_{:?}", event)
146                }
147            },
148            Origin::UndoRedo(event) => format!("undo_redo_{:?}", event),
149            Origin::LongOperation(event) => format!("long_operation_{:?}", event),
150            // features
151            Origin::HandlingAppLifecycle(event) => format!("handling_app_lifecycle_{:?}", event),
152            Origin::HandlingManifest(event) => format!("handling_manifest_{:?}", event),
153            Origin::RustFileGeneration(event) => format!("rust_file_generation_{:?}", event),
154            Origin::CppQtFileGeneration(event) => format!("cpp_qt_file_generation_{:?}", event),
155            Origin::FileGenerationSharedSteps(event) => {
156                format!("file_generation_shared_steps_{:?}", event)
157            }
158        }
159        .to_lowercase()
160    }
161}
162/// Thread-safe event buffer for deferring event emissions during transactions.
163///
164/// Repositories push events into this buffer instead of sending them directly
165/// to the EventHub. On commit(), the UoW drains the buffer and sends all events.
166/// On rollback(), the buffer is discarded. This prevents the UI from seeing
167/// phantom state from failed transactions.
168///
169/// This is the Rust equivalent of SignalBuffer in the C++/Qt target.
170pub struct EventBuffer {
171    buffering: bool,
172    pending: Vec<Event>,
173}
174
175impl EventBuffer {
176    pub fn new() -> Self {
177        Self {
178            buffering: false,
179            pending: Vec::new(),
180        }
181    }
182
183    /// Start buffering. Clears any stale events from a previous cycle.
184    pub fn begin_buffering(&mut self) {
185        self.buffering = true;
186        self.pending.clear();
187    }
188
189    /// Queue an event for deferred delivery.
190    ///
191    /// If buffering is not active, the event is silently dropped.
192    /// (Callers should only push during an active transaction.)
193    pub fn push(&mut self, event: Event) {
194        if self.buffering {
195            self.pending.push(event);
196        }
197    }
198
199    /// Drain all pending events and stop buffering.
200    /// The caller is responsible for sending them to the EventHub.
201    pub fn flush(&mut self) -> Vec<Event> {
202        self.buffering = false;
203        std::mem::take(&mut self.pending)
204    }
205
206    /// Discard all pending events and stop buffering.
207    pub fn discard(&mut self) {
208        self.buffering = false;
209        self.pending.clear();
210    }
211
212    pub fn is_buffering(&self) -> bool {
213        self.buffering
214    }
215}
216
217impl Default for EventBuffer {
218    fn default() -> Self {
219        Self::new()
220    }
221}
222
223pub type Queue = Arc<Mutex<Vec<Event>>>;
224
225/// Central event hub for managing subscriptions and dispatching events
226pub struct EventHub {
227    sender: Sender<Event>,
228    receiver: Receiver<Event>,
229    queue: Queue,
230}
231
232impl Default for EventHub {
233    fn default() -> Self {
234        Self::new()
235    }
236}
237
238impl EventHub {
239    /// Create a new event hub
240    pub fn new() -> Self {
241        let (sender, receiver) = unbounded();
242        EventHub {
243            sender,
244            receiver,
245            queue: Arc::new(Mutex::new(Vec::new())),
246        }
247    }
248
249    /// Start the event processing loop.
250    ///
251    /// Returns a `JoinHandle` so the caller can join the thread on shutdown.
252    /// The loop checks `stop_signal` between receives via a timeout, ensuring
253    /// it will exit even if no events arrive.
254    pub fn start_event_loop(&self, stop_signal: Arc<AtomicBool>) -> thread::JoinHandle<()> {
255        let receiver = self.receiver.clone();
256        let queue = self.queue.clone();
257        thread::spawn(move || {
258            loop {
259                if stop_signal.load(std::sync::atomic::Ordering::Relaxed) {
260                    break;
261                }
262
263                match receiver.recv_timeout(std::time::Duration::from_millis(100)) {
264                    Ok(event) => {
265                        let mut queue = queue.lock().unwrap();
266                        queue.push(event.clone());
267                    }
268                    Err(flume::RecvTimeoutError::Timeout) => {
269                        // Check stop_signal on next iteration
270                        continue;
271                    }
272                    Err(flume::RecvTimeoutError::Disconnected) => {
273                        break;
274                    }
275                };
276            }
277        })
278    }
279
280    /// Send an event to the queue
281    pub fn send_event(&self, event: Event) {
282        if let Err(e) = self.sender.send(event) {
283            eprintln!("EventHub: failed to send event (receiver dropped): {e}");
284        }
285    }
286
287    pub fn get_queue(&self) -> Queue {
288        self.queue.clone()
289    }
290
291    /// Get a direct event receiver.
292    ///
293    /// Each cloned receiver gets every event sent through the hub.
294    /// The receiver blocks on `recv()` until an event arrives — no polling needed.
295    /// Multiple receivers can coexist; each one independently receives all events.
296    pub fn subscribe_receiver(&self) -> Receiver<Event> {
297        self.receiver.clone()
298    }
299}
300
301#[cfg(test)]
302mod tests {
303    use super::*;
304
305    #[test]
306    fn test_event_hub_send_and_receive() {
307        let event_hub = EventHub::new();
308        let stop_signal = Arc::new(AtomicBool::new(false));
309        let _handle = event_hub.start_event_loop(stop_signal.clone());
310
311        let event = Event {
312            origin: Origin::DirectAccess(DirectAccessEntity::All(AllEvent::Reset)),
313            ids: vec![EntityId::default()],
314            data: Some("test_data".to_string()),
315        };
316
317        event_hub.send_event(event.clone());
318
319        thread::sleep(std::time::Duration::from_millis(100));
320
321        let queue = event_hub.get_queue();
322        let queue = queue.lock().unwrap();
323        assert_eq!(queue.len(), 1);
324        assert_eq!(queue[0], event);
325
326        stop_signal.store(true, std::sync::atomic::Ordering::Relaxed);
327    }
328}