jay_config/
timer.rs

1//! Timers for one-time or repeated actions.
2
3use {
4    serde::{Deserialize, Serialize},
5    std::time::{Duration, SystemTime, UNIX_EPOCH},
6};
7
8/// A timer.
9#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
10pub struct Timer(pub u64);
11
12/// Creates a new timer or returns an existing one.
13///
14/// Timers are identified by their name and their lifetime is bound by the lifetime of
15/// the configuration. Reloading the configuration destroys all existing timers.
16///
17/// Within the same configuration, calling this function multiple times with the same name
18/// will return the same timer.
19///
20/// Timers can be deleted by calling `remove`. At that point all existing references to
21/// the timer become invalid and `get_timer` will return a new timer.
22pub fn get_timer(name: &str) -> Timer {
23    get!(Timer(0)).get_timer(name)
24}
25
26impl Timer {
27    /// Programs the timer to fire once.
28    pub fn once(self, initial: Duration) {
29        get!().program_timer(self, Some(initial), None);
30    }
31
32    /// Programs the timer to fire repeatedly.
33    ///
34    /// `initial` is the period after which the timer expires for the first time.
35    pub fn repeated(self, initial: Duration, period: Duration) {
36        get!().program_timer(self, Some(initial), Some(period));
37    }
38
39    /// Cancels the timer.
40    ///
41    /// The timer remains valid but will never expire. It can be reprogrammed by calling
42    /// `once` or `repeated`.
43    pub fn cancel(self) {
44        get!().program_timer(self, None, None);
45    }
46
47    /// Removes the time.
48    ///
49    /// This reference to the timer becomes invalid as do all other existing references.
50    /// A new timer with the same name can be created by calling `get_timer`.
51    pub fn remove(self) {
52        get!().remove_timer(self);
53    }
54
55    /// Sets the function to be executed when the timer expires.
56    pub fn on_tick<F: FnMut() + 'static>(self, f: F) {
57        get!().on_timer_tick(self, f);
58    }
59}
60
61/// Returns the duration until the wall clock is a multiple of `duration`.
62///
63/// # Example
64///
65/// Execute a timer every time the wall clock becomes a multiple of 5 seconds:
66///
67/// ```rust,ignore
68/// let period = Duration::from_secs(5);
69/// let timer = get_timer("status_timer");
70/// timer.repeated(
71///     duration_until_wall_clock_is_multiple_of(period),
72///     period,
73/// );
74/// timer.on_tick(|| todo!());
75/// ```
76pub fn duration_until_wall_clock_is_multiple_of(duration: Duration) -> Duration {
77    let now = match SystemTime::now().duration_since(UNIX_EPOCH) {
78        Ok(n) => n,
79        _ => return Duration::from_secs(0),
80    };
81    let now = now.as_nanos();
82    let duration = duration.as_nanos();
83    if duration == 0 {
84        return Duration::from_secs(0);
85    }
86    let nanos = duration - now % duration;
87    if nanos == duration {
88        Duration::from_secs(0)
89    } else {
90        Duration::from_nanos(nanos as _)
91    }
92}