oxide_mvu/
emitter.rs

1//! Event emitter for embedding callbacks in Props.
2
3use async_channel::Sender;
4
5use crate::Event as EventTrait;
6
7/// Event emitter that can be embedded in Props.
8///
9/// Clone this handle to create callbacks in your Props that can trigger
10/// events when invoked (e.g., by user interaction).
11///
12/// `Emitter` wraps a lock-free MPMC channel sender, making it cheap to clone
13/// and thread-safe without any locking overhead.
14///
15/// # Example
16///
17/// ```rust
18/// use oxide_mvu::{Emitter, MvuLogic, Effect};
19///
20/// #[derive(Clone)]
21/// enum Event { Click }
22///
23/// #[derive(Clone)]
24/// struct Model { clicks: u32 }
25///
26/// struct Props {
27///     clicks: u32,
28///     on_click: Box<dyn Fn()>,
29/// }
30///
31/// struct MyApp;
32///
33/// impl MvuLogic<Event, Model, Props> for MyApp {
34///     fn init(&self, model: Model) -> (Model, Effect<Event>) {
35///         (model, Effect::none())
36///     }
37///
38///     fn update(&self, event: Event, model: &Model) -> (Model, Effect<Event>) {
39///         match event {
40///             Event::Click => {
41///                 let new_model = Model {
42///                     clicks: model.clicks + 1,
43///                     ..model.clone()
44///                 };
45///                 (new_model, Effect::none())
46///             }
47///         }
48///     }
49///
50///     fn view(&self, model: &Model, emitter: &Emitter<Event>) -> Props {
51///         let emitter = emitter.clone();
52///         Props {
53///             clicks: model.clicks,
54///             on_click: Box::new(move || {
55///                 emitter.try_emit(Event::Click);
56///             }),
57///         }
58///     }
59/// }
60/// ```
61pub struct Emitter<Event: EventTrait>(pub(crate) Sender<Event>);
62
63impl<Event: EventTrait> Clone for Emitter<Event> {
64    fn clone(&self) -> Self {
65        Self(self.0.clone())
66    }
67}
68
69impl<Event: EventTrait> Emitter<Event> {
70    /// Create a new emitter from a channel sender.
71    pub(crate) fn new(sender: Sender<Event>) -> Self {
72        Self(sender)
73    }
74
75    /// Emit an event without blocking.
76    ///
77    /// This attempts to queue the event for processing by the runtime. If the
78    /// event queue is full, the event will be dropped and `false` is returned.
79    ///
80    /// Multiple threads can safely call this method concurrently via the lock-free channel.
81    pub fn try_emit(&self, event: Event) -> bool {
82        self.0.try_send(event).is_ok()
83    }
84
85    /// Emit an event, waiting until space is available.
86    ///
87    /// This queues the event for processing by the runtime. If the queue is full,
88    /// this method will await until space becomes available.
89    ///
90    /// Multiple threads can safely call this method concurrently via the lock-free channel.
91    pub async fn emit(&self, event: Event) {
92        self.0.send(event).await.ok();
93    }
94}