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}