zest_core/time.rs
1//! Time-based subscription primitives.
2//!
3//! [`every`] is the common case — fire a message every N. It wraps the
4//! built-in [`Tick`] recipe so users don't have to define their own.
5
6use crate::application::{BoxFuture, Recipe, Subscription};
7use alloc::boxed::Box;
8use core::hash::{Hash, Hasher};
9use embassy_time::{Duration, Timer};
10
11/// Periodic-tick [`Recipe`]: emit `message.clone()` every `duration`.
12///
13/// Identity = `TypeId::of::<Tick<M>>()` ⊕ `Hash` of `duration` and
14/// `message`, so two ticks with different durations or different
15/// messages are distinct subscriptions.
16///
17/// Users usually call [`every`] rather than constructing this
18/// directly.
19pub struct Tick<M>
20where
21 M: Clone + 'static,
22{
23 /// Tick period.
24 pub duration: Duration,
25 /// Message to emit on each tick.
26 pub message: M,
27}
28
29impl<M> Hash for Tick<M>
30where
31 M: Clone + Hash + 'static,
32{
33 fn hash<H: Hasher>(&self, state: &mut H) {
34 self.duration.as_ticks().hash(state);
35 self.message.hash(state);
36 }
37}
38
39impl<M> Recipe for Tick<M>
40where
41 M: Clone + Hash + 'static,
42{
43 type Message = M;
44
45 fn next(&mut self) -> Option<BoxFuture<M>> {
46 let duration = self.duration;
47 let message = self.message.clone();
48 Some(Box::pin(async move {
49 Timer::after(duration).await;
50 message
51 }))
52 }
53}
54
55/// Emit `message` every `duration`.
56///
57/// Equivalent to `Subscription::from_recipe(Tick { duration, message })`.
58///
59/// ```ignore
60/// fn subscription(&self) -> Subscription<Msg> {
61/// if self.auto {
62/// zest_core::time::every(Duration::from_secs(1), Msg::Tick)
63/// } else {
64/// Subscription::none()
65/// }
66/// }
67/// ```
68#[must_use]
69pub fn every<M>(duration: Duration, message: M) -> Subscription<M>
70where
71 M: Clone + Hash + 'static,
72{
73 Subscription::from_recipe(Tick { duration, message })
74}