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}