text_document_frontend/app_context.rs
1// Generated by Qleany v1.7.3 from frontend_app_context.tera
2
3use common::database::db_context::DbContext;
4use common::event::EventHub;
5use common::long_operation::LongOperationManager;
6use common::undo_redo::UndoRedoManager;
7use flume::{Receiver, Sender};
8use parking_lot::Mutex;
9use std::sync::Arc;
10
11/// Application context that holds all shared state.
12///
13/// Shutdown lifecycle: when [`AppContext::shutdown`] is called the shared
14/// `shutdown_tx` is dropped, which makes every cloned `shutdown_rx`
15/// (held by `EventHubClient::start` threads) see `Disconnected` on its
16/// next `recv()` and exit. No polling and no atomic flag needed.
17#[derive(Clone, Debug)]
18pub struct AppContext {
19 pub db_context: DbContext,
20 pub event_hub: Arc<EventHub>,
21 /// Receiver clones are handed to background event-hub threads.
22 /// Threads exit when the matching sender is dropped via
23 /// [`AppContext::shutdown`].
24 pub shutdown_rx: Receiver<()>,
25 /// Shared single sender. `shutdown()` takes it out, dropping
26 /// the only live `Sender` and unblocking every receiver clone.
27 /// Wrapped in `Mutex<Option<…>>` so `shutdown()` stays `&self`
28 /// (the alternative — store the sender directly in `AppContext`
29 /// — would force every clone to hold its own sender clone and
30 /// `Disconnect` would only fire after the last clone dropped,
31 /// defeating the explicit `shutdown()` API).
32 shutdown_tx: Arc<Mutex<Option<Sender<()>>>>,
33 pub undo_redo_manager: Arc<Mutex<UndoRedoManager>>,
34 pub long_operation_manager: Arc<Mutex<LongOperationManager>>,
35}
36
37impl AppContext {
38 pub fn new() -> Self {
39 let db_context = DbContext::new().expect("Failed to create database context");
40
41 let event_hub = Arc::new(EventHub::new());
42 // Bounded(1) is enough — we only ever drop the sender, never
43 // actually send a unit on this channel. The bound keeps the
44 // allocation tiny.
45 let (shutdown_tx, shutdown_rx) = flume::bounded(1);
46
47 let undo_redo_manager = Arc::new(Mutex::new(UndoRedoManager::new()));
48 let long_operation_manager = Arc::new(Mutex::new(LongOperationManager::new()));
49
50 // Inject event hub into long_operation_manager
51 {
52 let mut lom = long_operation_manager.lock();
53 lom.set_event_hub(&event_hub);
54 }
55
56 // And into undo_redo_manager, for the same reason and at the same
57 // moment. It used to be injected lazily, by whichever undo/redo command
58 // happened to run first — which meant the *push* events never reached
59 // anyone: nothing pushes through those commands, so on a context where
60 // the user had only ever edited, `StackChanged` was emitted into a
61 // `None` hub and dropped. A subscriber cannot learn that "can undo" just
62 // became true from an event that is only delivered after the first undo.
63 {
64 let mut urm = undo_redo_manager.lock();
65 urm.set_event_hub(&event_hub);
66 }
67
68 Self {
69 db_context,
70 event_hub,
71 shutdown_rx,
72 shutdown_tx: Arc::new(Mutex::new(Some(shutdown_tx))),
73 undo_redo_manager,
74 long_operation_manager,
75 }
76 }
77
78 /// Signal background event-hub threads to stop. Idempotent — the
79 /// second call is a no-op because the sender has already been
80 /// taken.
81 pub fn shutdown(&self) {
82 let _ = self.shutdown_tx.lock().take();
83 }
84}
85
86impl Default for AppContext {
87 fn default() -> Self {
88 Self::new()
89 }
90}