Skip to main content

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}