Skip to main content

jay_config/
timer.rs

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