//! Timers for one-time or repeated actions. use { serde::{Deserialize, Serialize}, std::time::{Duration, SystemTime, UNIX_EPOCH}, }; /// A timer. #[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)] pub struct Timer(pub u64); /// Creates a new timer or returns an existing one. /// /// Timers are identified by their name and their lifetime is bound by the lifetime of /// the configuration. Reloading the configuration destroys all existing timers. /// /// Within the same configuration, calling this function multiple times with the same name /// will return the same timer. /// /// Timers can be deleted by calling `remove`. At that point all existing references to /// the timer become invalid and `get_timer` will return a new timer. pub fn get_timer(name: &str) -> Timer { get!(Timer(0)).get_timer(name) } impl Timer { /// Programs the timer to fire once. pub fn once(self, initial: Duration) { get!().program_timer(self, Some(initial), None); } /// Programs the timer to fire repeatedly. /// /// `initial` is the period after which the timer expires for the first time. pub fn repeated(self, initial: Duration, period: Duration) { get!().program_timer(self, Some(initial), Some(period)); } /// Cancels the timer. /// /// The timer remains valid but will never expire. It can be reprogrammed by calling /// `once` or `repeated`. pub fn cancel(self) { get!().program_timer(self, None, None); } /// Removes the time. /// /// This reference to the timer becomes invalid as do all other existing references. /// A new timer with the same name can be created by calling `get_timer`. pub fn remove(self) { get!().remove_timer(self); } /// Sets the function to be executed when the timer expires. pub fn on_tick(self, f: F) { get!().on_timer_tick(self, f); } } /// Returns the duration until the wall clock is a multiple of `duration`. /// /// # Example /// /// Execute a timer every time the wall clock becomes a multiple of 5 seconds: /// /// ```rust,ignore /// let period = Duration::from_secs(5); /// let timer = get_timer("status_timer"); /// timer.repeated( /// duration_until_wall_clock_is_multiple_of(period), /// period, /// ); /// timer.on_tick(|| todo!()); /// ``` pub fn duration_until_wall_clock_is_multiple_of(duration: Duration) -> Duration { let now = match SystemTime::now().duration_since(UNIX_EPOCH) { Ok(n) => n, _ => return Duration::from_secs(0), }; let now = now.as_nanos(); let duration = duration.as_nanos(); if duration == 0 { return Duration::from_secs(0); } let nanos = duration - now % duration; if nanos == duration { Duration::from_secs(0) } else { Duration::from_nanos(nanos as _) } }