reovim_kernel/ipc/context.rs
1//! Handler context for event dispatch.
2//!
3//! `HandlerContext` provides event handlers with the ability to:
4//! - Emit new events (with automatic scope inheritance)
5//! - Request render updates
6//! - Request application quit
7//!
8//! # Design Philosophy
9//!
10//! Following "mechanism, not policy":
11//! - Context provides emission and signaling mechanisms
12//! - Policy-specific methods (mode changes, etc.) stay in modules as extension traits
13//!
14//! # Example
15//!
16//! ```ignore
17//! bus.subscribe_with_context::<MyEvent, _>(100, |event, ctx| {
18//! // Emit a follow-up event (scope is inherited automatically)
19//! ctx.emit(FollowUpEvent { id: event.id });
20//!
21//! // Request a render after processing
22//! ctx.request_render();
23//!
24//! EventResult::Handled
25//! });
26//! ```
27
28use super::{
29 event::{DynEvent, Event, EventResult},
30 event_bus::EventSender,
31 scope::EventScope,
32};
33
34/// Context passed to event handlers during dispatch.
35///
36/// `HandlerContext` enables handlers to interact with the event system:
37/// - Emit new events with automatic scope tracking
38/// - Signal render requests
39/// - Signal quit requests
40///
41/// # Emission Modes
42///
43/// `HandlerContext` supports two emission modes:
44///
45/// 1. **Collect mode** (default): Events are collected in a Vec and returned
46/// via `take_emitted_events()` after the handler completes.
47///
48/// 2. **Direct mode** (with sender): Events are sent directly to a channel
49/// via the attached `EventSender`. Use `with_sender()` to enable this.
50///
51/// # Scope Inheritance
52///
53/// When `emit()` is called and the context has an attached scope,
54/// the new event inherits the scope. This ensures proper lifecycle
55/// tracking for chains of related events.
56///
57/// # Thread Safety
58///
59/// `HandlerContext` is not `Send` or `Sync` - it's designed to be used
60/// within a single handler invocation.
61pub struct HandlerContext<'a> {
62 /// Flag: render was requested by handler
63 render_requested: bool,
64
65 /// Flag: quit was requested by handler
66 quit_requested: bool,
67
68 /// Current scope for lifecycle tracking (inherited by emitted events)
69 scope: Option<EventScope>,
70
71 /// Events emitted during handler execution (collect mode)
72 /// These are collected and dispatched after the handler returns
73 emitted_events: Vec<DynEvent>,
74
75 /// Optional sender for direct emission mode
76 /// When present, `emit()` sends directly instead of collecting
77 sender: Option<&'a EventSender>,
78}
79
80impl<'a> HandlerContext<'a> {
81 /// Create a new handler context in collect mode.
82 ///
83 /// Events emitted via `emit()` are collected and can be retrieved
84 /// with `take_emitted_events()` after the handler completes.
85 ///
86 /// Typically called by the event processor before invoking handlers.
87 #[must_use]
88 pub const fn new() -> Self {
89 Self {
90 render_requested: false,
91 quit_requested: false,
92 scope: None,
93 emitted_events: Vec::new(),
94 sender: None,
95 }
96 }
97
98 /// Attach an event sender for direct emission mode.
99 ///
100 /// When a sender is attached, `emit()` sends events directly to the
101 /// channel instead of collecting them. This matches the runtime's
102 /// expected behavior where emitted events re-enter the event queue.
103 ///
104 /// # Example
105 ///
106 /// ```ignore
107 /// let sender = bus.sender().unwrap();
108 /// let ctx = HandlerContext::new().with_sender(&sender);
109 /// // Now ctx.emit() sends directly via channel
110 /// ```
111 #[must_use]
112 pub const fn with_sender(mut self, sender: &'a EventSender) -> Self {
113 self.sender = Some(sender);
114 self
115 }
116
117 /// Attach a scope for lifecycle tracking.
118 ///
119 /// Events emitted via `emit()` will inherit this scope.
120 #[must_use]
121 pub fn with_scope(mut self, scope: Option<EventScope>) -> Self {
122 self.scope = scope;
123 self
124 }
125
126 /// Get a reference to the current scope, if any.
127 #[must_use]
128 pub const fn scope(&self) -> Option<&EventScope> {
129 self.scope.as_ref()
130 }
131
132 /// Emit a new event from within a handler.
133 ///
134 /// Behavior depends on mode:
135 /// - **Collect mode** (no sender): Event is collected in internal Vec
136 /// - **Direct mode** (with sender): Event is sent directly to channel
137 ///
138 /// If this context has a scope, the event inherits it for proper
139 /// lifecycle tracking.
140 ///
141 /// # Scope Tracking
142 ///
143 /// When a scope is present:
144 /// 1. `scope.increment()` is called
145 /// 2. Event is wrapped with the scope
146 /// 3. After dispatch, `scope.decrement()` is called by the processor
147 ///
148 /// # Example
149 ///
150 /// ```ignore
151 /// ctx.emit(BufferModified { buffer_id: 1 });
152 /// ```
153 pub fn emit<E: Event>(&mut self, event: E) {
154 let dyn_event = if let Some(ref scope) = self.scope {
155 scope.increment();
156 DynEvent::new(event).with_scope(scope.clone())
157 } else {
158 DynEvent::new(event)
159 };
160
161 // Direct mode: send via channel
162 if let Some(sender) = self.sender {
163 sender.send_dyn(dyn_event);
164 } else {
165 // Collect mode: store for later retrieval
166 self.emitted_events.push(dyn_event);
167 }
168 }
169
170 /// Emit a pre-boxed dynamic event.
171 ///
172 /// Useful when you already have a `DynEvent` and want to emit it.
173 /// The event's existing scope (if any) is preserved.
174 pub fn emit_dyn(&mut self, event: DynEvent) {
175 if let Some(sender) = self.sender {
176 sender.send_dyn(event);
177 } else {
178 self.emitted_events.push(event);
179 }
180 }
181
182 /// Request a render update after event processing completes.
183 ///
184 /// This is a hint to the runtime that the UI should be refreshed.
185 /// The actual render is performed by the runtime, not the kernel.
186 pub const fn request_render(&mut self) {
187 self.render_requested = true;
188 }
189
190 /// Request application quit.
191 ///
192 /// This signals that the handler wants the application to terminate.
193 /// The runtime decides how to handle this request.
194 pub const fn request_quit(&mut self) {
195 self.quit_requested = true;
196 }
197
198 /// Check if render was requested.
199 #[must_use]
200 pub const fn render_requested(&self) -> bool {
201 self.render_requested
202 }
203
204 /// Check if quit was requested.
205 #[must_use]
206 pub const fn quit_requested(&self) -> bool {
207 self.quit_requested
208 }
209
210 /// Take collected emitted events.
211 ///
212 /// Called by the event processor after handler execution to get
213 /// the events that need to be dispatched.
214 ///
215 /// Note: In direct mode (with sender), this will always return
216 /// an empty Vec since events are sent immediately.
217 #[must_use]
218 pub fn take_emitted_events(&mut self) -> Vec<DynEvent> {
219 std::mem::take(&mut self.emitted_events)
220 }
221
222 /// Check if any events were emitted (collect mode only).
223 #[must_use]
224 pub const fn has_emitted_events(&self) -> bool {
225 !self.emitted_events.is_empty()
226 }
227
228 /// Get the number of emitted events (collect mode only).
229 #[must_use]
230 pub const fn emitted_event_count(&self) -> usize {
231 self.emitted_events.len()
232 }
233
234 /// Check if this context is in direct emission mode.
235 #[must_use]
236 pub const fn has_sender(&self) -> bool {
237 self.sender.is_some()
238 }
239}
240
241impl Default for HandlerContext<'_> {
242 fn default() -> Self {
243 Self::new()
244 }
245}
246
247impl std::fmt::Debug for HandlerContext<'_> {
248 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249 f.debug_struct("HandlerContext")
250 .field("render_requested", &self.render_requested)
251 .field("quit_requested", &self.quit_requested)
252 .field("has_scope", &self.scope.is_some())
253 .field("has_sender", &self.sender.is_some())
254 .field("emitted_events", &self.emitted_events.len())
255 .finish()
256 }
257}
258
259/// Result of dispatching with context.
260///
261/// Contains both the event result and any side effects from handlers.
262#[derive(Debug)]
263pub struct DispatchResult {
264 /// The event handling result
265 pub result: EventResult,
266
267 /// Whether any handler requested a render
268 pub render_requested: bool,
269
270 /// Whether any handler requested quit
271 pub quit_requested: bool,
272
273 /// Events emitted by handlers (need to be dispatched)
274 pub emitted_events: Vec<DynEvent>,
275}
276
277impl DispatchResult {
278 /// Create a new dispatch result.
279 #[must_use]
280 pub fn new(result: EventResult, ctx: &mut HandlerContext<'_>) -> Self {
281 Self {
282 result,
283 render_requested: ctx.render_requested(),
284 quit_requested: ctx.quit_requested(),
285 emitted_events: ctx.take_emitted_events(),
286 }
287 }
288
289 /// Create a result indicating no handlers matched.
290 #[must_use]
291 pub const fn not_handled() -> Self {
292 Self {
293 result: EventResult::NotHandled,
294 render_requested: false,
295 quit_requested: false,
296 emitted_events: Vec::new(),
297 }
298 }
299}