reovim_kernel/ipc/event.rs
1//! Event trait and type-erased event wrapper.
2//!
3//! This module provides the foundational `Event` trait that all IPC events must implement,
4//! along with `DynEvent` for type-erased event dispatch.
5//!
6//! # Design Philosophy
7//!
8//! Following the Linux kernel "mechanism, not policy" principle:
9//! - Event trait defines minimal requirements
10//! - `DynEvent` provides type-erased storage and dispatch
11//! - `EventResult` signals handler outcomes without error semantics
12//!
13//! # Example
14//!
15//! ```
16//! use reovim_kernel::api::v1::*;
17//!
18//! #[derive(Debug)]
19//! struct BufferChanged {
20//! buffer_id: u64,
21//! }
22//!
23//! impl Event for BufferChanged {
24//! fn priority(&self) -> u32 { 50 } // Core priority
25//! }
26//!
27//! let event = BufferChanged { buffer_id: 1 };
28//! let dyn_event = DynEvent::new(event);
29//!
30//! // Type-safe downcasting
31//! if let Some(bc) = dyn_event.downcast_ref::<BufferChanged>() {
32//! assert_eq!(bc.buffer_id, 1);
33//! }
34//! ```
35
36use std::{
37 any::{Any, TypeId},
38 fmt::Debug,
39};
40
41use super::scope::EventScope;
42
43/// Trait for all IPC events.
44///
45/// Events are the primary communication mechanism between kernel subsystems,
46/// drivers, and modules. All events must be:
47/// - `Send + Sync` for cross-thread dispatch
48/// - `Debug` for logging and diagnostics
49/// - `'static` for type-erased storage
50///
51/// # Priority System
52///
53/// Events have a priority that affects handler dispatch order:
54/// - **0-50**: Core/critical handlers (run first)
55/// - **100**: Default priority
56/// - **200+**: Low priority (cleanup, logging)
57///
58/// Lower priority numbers mean earlier dispatch.
59///
60/// # Batching
61///
62/// Events can opt into batching for future optimization. When `batchable()`
63/// returns `true`, the event bus may combine multiple events of the same type
64/// into a single dispatch when under load.
65///
66/// # Targeted Events
67///
68/// For events that target specific components, implement [`TargetedEvent`]
69/// in addition to `Event`. This allows using `EventBus::subscribe_targeted()`
70/// for automatic filtering by target.
71pub trait Event: Send + Sync + Debug + 'static {
72 /// Priority for handler dispatch ordering.
73 ///
74 /// Lower values = earlier dispatch. Default is 100 (normal priority).
75 ///
76 /// Convention:
77 /// - 0-50: Core handlers
78 /// - 100: Default/plugin handlers
79 /// - 200+: Low priority (logging, cleanup)
80 fn priority(&self) -> u32 {
81 100
82 }
83
84 /// Whether this event can be batched with similar events.
85 ///
86 /// Default is `false`. When `true`, the event bus may combine multiple
87 /// events of the same type into a single dispatch for efficiency.
88 fn batchable(&self) -> bool {
89 false
90 }
91}
92
93/// Marker trait for events that target a specific component.
94///
95/// Events implementing this trait have a `target` field that specifies
96/// which component should handle the event. This enables `EventBus::subscribe_targeted()`
97/// to automatically filter events by target.
98///
99/// # Design Note
100///
101/// The kernel uses `&str` for target identifiers (mechanism), not `ComponentId`
102/// (which is a policy-level type). Modules convert their identifiers to `&str`
103/// when interacting with the kernel API.
104///
105/// # Example
106///
107/// ```
108/// use reovim_kernel::api::v1::*;
109///
110/// #[derive(Debug)]
111/// struct PluginTextInput {
112/// target: &'static str,
113/// c: char,
114/// }
115///
116/// impl Event for PluginTextInput {}
117///
118/// impl TargetedEvent for PluginTextInput {
119/// fn target(&self) -> &str {
120/// self.target
121/// }
122/// }
123/// ```
124pub trait TargetedEvent: Event {
125 /// Get the target component identifier for this event.
126 ///
127 /// Used by `EventBus::subscribe_targeted()` to filter events.
128 fn target(&self) -> &str;
129}
130
131/// Result of handling an event.
132///
133/// Unlike `Result<T, E>`, this enum has no error variant. Event handling follows
134/// a fire-and-forget philosophy where handlers must not fail - they either
135/// handle the event or pass it on.
136///
137/// # Handler Behavior
138///
139/// - `Handled`: Continue to next handler (most common)
140/// - `Consumed`: Stop propagation to remaining handlers
141/// - `NotHandled`: Handler didn't process this event (continue dispatch)
142#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
143pub enum EventResult {
144 /// Event was handled, continue to next handler.
145 Handled,
146
147 /// Event was consumed, stop propagation.
148 Consumed,
149
150 /// Handler did not process this event.
151 #[default]
152 NotHandled,
153}
154
155impl EventResult {
156 /// Returns `true` if the event was consumed (propagation should stop).
157 #[inline]
158 #[must_use]
159 pub const fn is_consumed(&self) -> bool {
160 matches!(self, Self::Consumed)
161 }
162
163 /// Returns `true` if the event was handled (but not consumed).
164 #[inline]
165 #[must_use]
166 pub const fn is_handled(&self) -> bool {
167 matches!(self, Self::Handled)
168 }
169
170 /// Returns `true` if the handler did not process the event.
171 #[inline]
172 #[must_use]
173 pub const fn is_not_handled(&self) -> bool {
174 matches!(self, Self::NotHandled)
175 }
176}
177
178/// Type-erased event wrapper for dynamic dispatch.
179///
180/// `DynEvent` wraps any type implementing `Event` and provides:
181/// - Type-safe downcasting via `TypeId`
182/// - Priority and metadata access
183/// - Optional scope attachment for lifecycle tracking
184///
185/// # Type Safety
186///
187/// Despite being type-erased, `DynEvent` maintains full type safety through
188/// `TypeId`-based downcasting. Incorrect type casts return `None` rather than
189/// undefined behavior.
190///
191/// # Memory Layout
192///
193/// ```text
194/// DynEvent
195/// ├── type_id: TypeId (16 bytes)
196/// ├── type_name: &'static str (16 bytes)
197/// ├── priority: u32 (4 bytes)
198/// ├── payload: Box<dyn Any + Send + Sync> (heap-allocated)
199/// └── scope: Option<EventScope> (optional lifecycle tracking)
200/// ```
201pub struct DynEvent {
202 /// `TypeId` for runtime type checking.
203 type_id: TypeId,
204
205 /// Type name for debugging/logging.
206 type_name: &'static str,
207
208 /// Event priority (cached from `Event::priority()`).
209 priority: u32,
210
211 /// Type-erased event payload
212 payload: Box<dyn Any + Send + Sync>,
213
214 /// Optional scope for lifecycle tracking
215 scope: Option<EventScope>,
216}
217
218impl DynEvent {
219 /// Create a new `DynEvent` from any type implementing `Event`.
220 ///
221 /// # Example
222 ///
223 /// ```
224 /// use reovim_kernel::api::v1::*;
225 ///
226 /// #[derive(Debug)]
227 /// struct MyEvent;
228 /// impl Event for MyEvent {}
229 ///
230 /// let dyn_event = DynEvent::new(MyEvent);
231 /// assert!(dyn_event.type_name().contains("MyEvent"));
232 /// ```
233 pub fn new<E: Event>(event: E) -> Self {
234 Self {
235 type_id: TypeId::of::<E>(),
236 type_name: std::any::type_name::<E>(),
237 priority: event.priority(),
238 payload: Box::new(event),
239 scope: None,
240 }
241 }
242
243 /// Attach an `EventScope` for lifecycle tracking.
244 ///
245 /// Returns `self` for method chaining.
246 ///
247 /// # Example
248 ///
249 /// ```
250 /// use reovim_kernel::api::v1::*;
251 ///
252 /// #[derive(Debug)]
253 /// struct MyEvent;
254 /// impl Event for MyEvent {}
255 ///
256 /// let scope = EventScope::new();
257 /// let event = DynEvent::new(MyEvent).with_scope(scope);
258 /// assert!(event.scope().is_some());
259 /// ```
260 #[must_use]
261 pub fn with_scope(mut self, scope: EventScope) -> Self {
262 self.scope = Some(scope);
263 self
264 }
265
266 /// Get the `TypeId` of the wrapped event.
267 #[inline]
268 #[must_use]
269 pub const fn type_id(&self) -> TypeId {
270 self.type_id
271 }
272
273 /// Get the type name of the wrapped event (for debugging).
274 #[inline]
275 #[must_use]
276 pub const fn type_name(&self) -> &'static str {
277 self.type_name
278 }
279
280 /// Get the event's priority.
281 #[inline]
282 #[must_use]
283 pub const fn priority(&self) -> u32 {
284 self.priority
285 }
286
287 /// Get a reference to the attached scope, if any.
288 #[inline]
289 #[must_use]
290 pub const fn scope(&self) -> Option<&EventScope> {
291 self.scope.as_ref()
292 }
293
294 /// Take the attached scope, leaving `None` in its place.
295 #[inline]
296 #[allow(clippy::missing_const_for_fn)] // Option::take() is not const
297 pub fn take_scope(&mut self) -> Option<EventScope> {
298 self.scope.take()
299 }
300
301 /// Check if this event is of type `E`.
302 #[inline]
303 #[must_use]
304 pub fn is<E: Event>(&self) -> bool {
305 self.type_id == TypeId::of::<E>()
306 }
307
308 /// Attempt to downcast to a reference of type `E`.
309 ///
310 /// Returns `None` if the event is not of type `E`.
311 ///
312 /// # Example
313 ///
314 /// ```
315 /// use reovim_kernel::api::v1::*;
316 ///
317 /// #[derive(Debug)]
318 /// struct MyEvent { value: i32 }
319 /// impl Event for MyEvent {}
320 ///
321 /// let event = DynEvent::new(MyEvent { value: 42 });
322 ///
323 /// if let Some(my_event) = event.downcast_ref::<MyEvent>() {
324 /// assert_eq!(my_event.value, 42);
325 /// }
326 /// ```
327 #[inline]
328 #[must_use]
329 pub fn downcast_ref<E: Event>(&self) -> Option<&E> {
330 if self.type_id == TypeId::of::<E>() {
331 self.payload.downcast_ref()
332 } else {
333 None
334 }
335 }
336
337 /// Attempt to downcast to a mutable reference of type `E`.
338 ///
339 /// Returns `None` if the event is not of type `E`.
340 #[inline]
341 #[must_use]
342 pub fn downcast_mut<E: Event>(&mut self) -> Option<&mut E> {
343 if self.type_id == TypeId::of::<E>() {
344 self.payload.downcast_mut()
345 } else {
346 None
347 }
348 }
349
350 /// Consume the `DynEvent` and attempt to extract the inner event.
351 ///
352 /// # Errors
353 ///
354 /// Returns `Err(self)` if the event is not of type `E`.
355 ///
356 /// # Panics
357 ///
358 /// This function will not panic under normal operation. The internal
359 /// `unwrap()` is guarded by a `TypeId` check that ensures type safety.
360 ///
361 /// # Example
362 ///
363 /// ```
364 /// use reovim_kernel::api::v1::*;
365 ///
366 /// #[derive(Debug, PartialEq)]
367 /// struct MyEvent { value: i32 }
368 /// impl Event for MyEvent {}
369 ///
370 /// let event = DynEvent::new(MyEvent { value: 42 });
371 ///
372 /// match event.into_inner::<MyEvent>() {
373 /// Ok(my_event) => assert_eq!(my_event.value, 42),
374 /// Err(_) => panic!("unexpected type"),
375 /// }
376 /// ```
377 pub fn into_inner<E: Event>(self) -> Result<E, Self> {
378 if self.type_id == TypeId::of::<E>() {
379 // SAFETY: We verified the type matches via TypeId
380 Ok(*self.payload.downcast::<E>().unwrap())
381 } else {
382 Err(self)
383 }
384 }
385}
386
387impl Debug for DynEvent {
388 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
389 f.debug_struct("DynEvent")
390 .field("type_name", &self.type_name)
391 .field("priority", &self.priority)
392 .field("has_scope", &self.scope.is_some())
393 .finish_non_exhaustive()
394 }
395}
396
397// DynEvent is automatically Send + Sync because:
398// - payload is Box<dyn Any + Send + Sync>
399// - EventScope is Arc-based and thread-safe
400// - All other fields are Copy or 'static references
401//
402// No manual unsafe impl needed - Rust derives these automatically.
403
404// ============================================================================
405// Built-in Events
406// ============================================================================
407
408use crate::mm::BufferId;
409
410/// Cache update notification event.
411///
412/// Emitted when a cache (e.g., syntax highlights) has been updated for a buffer.
413/// Drivers can subscribe to this event to trigger re-renders or other updates.
414///
415/// # Example
416///
417/// ```ignore
418/// use reovim_kernel::api::v1::*;
419///
420/// let bus = EventBus::new();
421///
422/// bus.subscribe::<CacheUpdated, _>(100, |event| {
423/// println!("Cache updated for buffer {:?}: {:?}", event.buffer_id, event.kind);
424/// EventResult::Handled
425/// });
426/// ```
427#[derive(Debug, Clone)]
428pub struct CacheUpdated {
429 /// The buffer whose cache was updated.
430 pub buffer_id: BufferId,
431 /// What kind of cache was updated.
432 pub kind: CacheKind,
433}
434
435impl Event for CacheUpdated {
436 fn priority(&self) -> u32 {
437 50 // Core priority
438 }
439}
440
441/// The kind of cache that was updated.
442#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
443pub enum CacheKind {
444 /// Syntax highlighting cache.
445 Highlights,
446 /// Decorations cache (diagnostics, git markers, etc.).
447 Decorations,
448 /// Both highlights and decorations.
449 Both,
450}