time/
timer.rs

1//! # Timer
2//!
3//! This module contains everything related to the timer. A timer can
4//! be identified by a state (running or stopped), a cycle and a
5//! cycles count (infinite or finite). During the lifetime of the
6//! timer, timer events are triggered.
7
8use log::debug;
9#[cfg(all(feature = "server", test))]
10use mock_instant::Instant;
11use serde::{Deserialize, Serialize};
12#[cfg(feature = "server")]
13use std::io::{Error, ErrorKind};
14#[cfg(all(feature = "server", not(test)))]
15use std::time::Instant;
16use std::{
17    fmt,
18    io::Result,
19    ops::{Deref, DerefMut},
20    sync::Arc,
21};
22#[cfg(feature = "server")]
23use tokio::sync::Mutex;
24
25use crate::handler::{self, Handler};
26
27/// The timer loop.
28///
29/// When the timer reaches its last cycle, it starts again from the
30/// first cycle. This structure defines the number of loops the timer
31/// should do before stopping by itself.
32#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
33pub enum TimerLoop {
34    /// The timer loops indefinitely and therefore never stops by
35    /// itself.
36    ///
37    /// The only way to stop such timer is via a stop request.
38    #[default]
39    Infinite,
40
41    /// The timer stops by itself after the given number of loops.
42    Fixed(usize),
43}
44
45impl From<usize> for TimerLoop {
46    fn from(count: usize) -> Self {
47        if count == 0 {
48            Self::Infinite
49        } else {
50            Self::Fixed(count)
51        }
52    }
53}
54
55/// The timer cycle.
56///
57/// A cycle is a step in the timer lifetime, represented by a name and
58/// a duration.
59#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
60pub struct TimerCycle {
61    /// The name of the timer cycle.
62    pub name: String,
63
64    /// The duration of the timer cycle.
65    ///
66    /// This field has two meanings, depending on where it is
67    /// used. *From the config point of view*, the duration represents
68    /// the total duration of the cycle. *From the timer point of
69    /// view*, the duration represents the amount of time remaining
70    /// before the cycle ends.
71    pub duration: usize,
72}
73
74impl TimerCycle {
75    pub fn new(name: impl ToString, duration: usize) -> Self {
76        Self {
77            name: name.to_string(),
78            duration,
79        }
80    }
81}
82
83impl<T: ToString> From<(T, usize)> for TimerCycle {
84    fn from((name, duration): (T, usize)) -> Self {
85        Self::new(name, duration)
86    }
87}
88
89/// The timer cycles list.
90#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
91pub struct TimerCycles(Vec<TimerCycle>);
92
93impl<T: IntoIterator<Item = TimerCycle>> From<T> for TimerCycles {
94    fn from(cycles: T) -> Self {
95        Self(cycles.into_iter().collect())
96    }
97}
98
99impl Deref for TimerCycles {
100    type Target = Vec<TimerCycle>;
101
102    fn deref(&self) -> &Self::Target {
103        &self.0
104    }
105}
106
107impl DerefMut for TimerCycles {
108    fn deref_mut(&mut self) -> &mut Self::Target {
109        &mut self.0
110    }
111}
112
113/// The timer state.
114///
115/// Enumeration of all the possible state of a timer: running, paused
116/// or stopped.
117#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
118pub enum TimerState {
119    /// The timer is running.
120    Running,
121
122    /// The timer has been paused.
123    Paused,
124
125    /// The timer is not running.
126    #[default]
127    Stopped,
128}
129
130/// The timer event.
131///
132/// Enumeration of all possible events that can be triggered during
133/// the timer lifecycle.
134#[derive(Clone, Debug, Eq, PartialEq)]
135pub enum TimerEvent {
136    /// The timer started.
137    Started,
138
139    /// The timer began the given cycle.
140    Began(TimerCycle),
141
142    /// The timer is running the given cycle (tick).
143    Running(TimerCycle),
144
145    /// The timer has been set to the given cycle.
146    Set(TimerCycle),
147
148    /// The timer has been paused at the given cycle.
149    Paused(TimerCycle),
150
151    /// The timer has been resumed at the given cycle.
152    Resumed(TimerCycle),
153
154    /// The timer ended with the given cycle.
155    Ended(TimerCycle),
156
157    /// The timer stopped.
158    Stopped,
159}
160
161/// The timer configuration.
162#[derive(Clone)]
163pub struct TimerConfig {
164    /// The list of custom timer cycles.
165    pub cycles: TimerCycles,
166
167    /// The timer cycles counter.
168    pub cycles_count: TimerLoop,
169
170    /// The timer event handler.
171    pub handler: Arc<Handler<TimerEvent>>,
172}
173
174impl Default for TimerConfig {
175    fn default() -> Self {
176        Self {
177            cycles: Default::default(),
178            cycles_count: Default::default(),
179            handler: handler::default(),
180        }
181    }
182}
183
184#[cfg(feature = "server")]
185impl TimerConfig {
186    fn clone_first_cycle(&self) -> Result<TimerCycle> {
187        self.cycles.first().cloned().ok_or_else(|| {
188            Error::new(
189                ErrorKind::NotFound,
190                "cannot find first cycle from timer config",
191            )
192        })
193    }
194}
195
196/// The main timer struct.
197#[derive(Clone, Default, Serialize, Deserialize)]
198pub struct Timer {
199    /// The current timer configuration.
200    #[serde(skip)]
201    pub config: TimerConfig,
202
203    /// The current timer state.
204    pub state: TimerState,
205
206    /// The current timer cycle.
207    pub cycle: TimerCycle,
208
209    /// The current cycles counter.
210    pub cycles_count: TimerLoop,
211
212    #[cfg(feature = "server")]
213    #[serde(skip)]
214    pub started_at: Option<Instant>,
215
216    #[cfg(feature = "server")]
217    pub elapsed: usize,
218}
219
220impl fmt::Debug for Timer {
221    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
222        let timer = serde_json::to_string(self).map_err(|_| fmt::Error)?;
223        write!(f, "{timer}")
224    }
225}
226
227impl Eq for Timer {}
228
229#[cfg(feature = "server")]
230impl PartialEq for Timer {
231    fn eq(&self, other: &Self) -> bool {
232        self.state == other.state && self.cycle == other.cycle && self.elapsed() == other.elapsed()
233    }
234}
235
236#[cfg(not(feature = "server"))]
237impl PartialEq for Timer {
238    fn eq(&self, other: &Self) -> bool {
239        self.state == other.state && self.cycle == other.cycle
240    }
241}
242
243#[cfg(feature = "server")]
244impl Timer {
245    pub fn elapsed(&self) -> usize {
246        self.started_at
247            .map(|i| i.elapsed().as_secs() as usize)
248            .unwrap_or_default()
249            + self.elapsed
250    }
251
252    pub async fn update(&mut self) {
253        let mut elapsed = self.elapsed();
254
255        match self.state {
256            TimerState::Running => {
257                let (cycles, total_duration) = self.config.cycles.iter().cloned().fold(
258                    (Vec::new(), 0),
259                    |(mut cycles, mut sum), mut cycle| {
260                        cycle.duration += sum;
261                        sum = cycle.duration;
262                        cycles.push(cycle);
263                        (cycles, sum)
264                    },
265                );
266
267                if let TimerLoop::Fixed(cycles_count) = self.cycles_count {
268                    if elapsed >= (total_duration * cycles_count) {
269                        self.state = TimerState::Stopped;
270                        return;
271                    }
272                }
273
274                elapsed = elapsed % total_duration;
275
276                let last_cycle = cycles[cycles.len() - 1].clone();
277                let next_cycle = cycles
278                    .into_iter()
279                    .fold(None, |next_cycle, mut cycle| match next_cycle {
280                        None if elapsed < cycle.duration => {
281                            cycle.duration = cycle.duration - elapsed;
282                            Some(cycle)
283                        }
284                        _ => next_cycle,
285                    })
286                    .unwrap_or(last_cycle);
287
288                self.fire_event(TimerEvent::Running(self.cycle.clone()))
289                    .await;
290
291                if self.cycle.name != next_cycle.name {
292                    let mut prev_cycle = self.cycle.clone();
293                    prev_cycle.duration = 0;
294                    self.fire_events([
295                        TimerEvent::Ended(prev_cycle),
296                        TimerEvent::Began(next_cycle.clone()),
297                    ])
298                    .await;
299                }
300
301                self.cycle = next_cycle;
302            }
303            TimerState::Paused => {
304                // nothing to do
305            }
306            TimerState::Stopped => {
307                // nothing to do
308            }
309        }
310    }
311
312    pub async fn fire_event(&self, event: TimerEvent) {
313        let handler = &self.config.handler;
314        debug!("firing timer event {event:?}");
315        if let Err(err) = handler(event.clone()).await {
316            debug!("cannot fire timer event, skipping it");
317            debug!("{err:?}");
318        }
319    }
320
321    pub async fn fire_events(&self, events: impl IntoIterator<Item = TimerEvent>) {
322        for event in events.into_iter() {
323            self.fire_event(event).await
324        }
325    }
326
327    pub async fn start(&mut self) -> Result<()> {
328        if matches!(self.state, TimerState::Stopped) {
329            self.state = TimerState::Running;
330            self.cycle = self.config.clone_first_cycle()?;
331            self.cycles_count = self.config.cycles_count.clone();
332            self.started_at = Some(Instant::now());
333            self.elapsed = 0;
334            self.fire_events([TimerEvent::Started, TimerEvent::Began(self.cycle.clone())])
335                .await;
336        }
337        Ok(())
338    }
339
340    pub async fn set(&mut self, duration: usize) -> Result<()> {
341        self.cycle.duration = duration;
342        self.fire_event(TimerEvent::Set(self.cycle.clone())).await;
343        Ok(())
344    }
345
346    pub async fn pause(&mut self) -> Result<()> {
347        if matches!(self.state, TimerState::Running) {
348            self.state = TimerState::Paused;
349            self.elapsed = self.elapsed();
350            self.started_at = None;
351            self.fire_event(TimerEvent::Paused(self.cycle.clone()))
352                .await;
353        }
354        Ok(())
355    }
356
357    pub async fn resume(&mut self) -> Result<()> {
358        if matches!(self.state, TimerState::Paused) {
359            self.state = TimerState::Running;
360            self.started_at = Some(Instant::now());
361            self.fire_event(TimerEvent::Resumed(self.cycle.clone()))
362                .await;
363        }
364        Ok(())
365    }
366
367    pub async fn stop(&mut self) -> Result<()> {
368        if matches!(self.state, TimerState::Running) {
369            self.state = TimerState::Stopped;
370            self.fire_events([TimerEvent::Ended(self.cycle.clone()), TimerEvent::Stopped])
371                .await;
372            self.cycle = self.config.clone_first_cycle()?;
373            self.cycles_count = self.config.cycles_count.clone();
374            self.started_at = None;
375            self.elapsed = 0;
376        }
377        Ok(())
378    }
379}
380
381/// Thread safe version of the [`Timer`].
382///
383/// The server does not manipulate directly the [`Timer`], it uses
384/// this thread safe version instead (mainly because the timer runs in
385/// a [`std::thread::spawn`] loop).
386#[cfg(feature = "server")]
387#[derive(Clone, Debug, Default)]
388pub struct ThreadSafeTimer(Arc<Mutex<Timer>>);
389
390#[cfg(feature = "server")]
391impl ThreadSafeTimer {
392    pub fn new(config: TimerConfig) -> Result<Self> {
393        let mut timer = Timer::default();
394
395        timer.config = config;
396        timer.cycle = timer.config.clone_first_cycle()?;
397        timer.cycles_count = timer.config.cycles_count.clone();
398
399        Ok(Self(Arc::new(Mutex::new(timer))))
400    }
401
402    pub async fn update(&self) {
403        self.0.lock().await.update().await;
404    }
405
406    pub async fn start(&self) -> Result<()> {
407        self.0.lock().await.start().await
408    }
409
410    pub async fn get(&self) -> Timer {
411        self.0.lock().await.clone()
412    }
413
414    pub async fn set(&self, duration: usize) -> Result<()> {
415        self.0.lock().await.set(duration).await
416    }
417
418    pub async fn pause(&self) -> Result<()> {
419        self.0.lock().await.pause().await
420    }
421
422    pub async fn resume(&self) -> Result<()> {
423        self.0.lock().await.resume().await
424    }
425
426    pub async fn stop(&self) -> Result<()> {
427        self.0.lock().await.stop().await
428    }
429}
430
431#[cfg(feature = "server")]
432impl Deref for ThreadSafeTimer {
433    type Target = Arc<Mutex<Timer>>;
434
435    fn deref(&self) -> &Self::Target {
436        &self.0
437    }
438}
439
440#[cfg(feature = "server")]
441impl DerefMut for ThreadSafeTimer {
442    fn deref_mut(&mut self) -> &mut Self::Target {
443        &mut self.0
444    }
445}
446
447#[cfg(test)]
448mod tests {
449    use mock_instant::{Instant, MockClock};
450    use once_cell::sync::Lazy;
451    use std::{sync::Arc, time::Duration};
452
453    use super::*;
454
455    fn testing_timer() -> Timer {
456        Timer {
457            config: TimerConfig {
458                cycles: TimerCycles::from([
459                    TimerCycle::new("a", 3),
460                    TimerCycle::new("b", 2),
461                    TimerCycle::new("c", 1),
462                ]),
463                ..Default::default()
464            },
465            state: TimerState::Running,
466            cycle: TimerCycle::new("a", 3),
467            started_at: Some(Instant::now()),
468            ..Default::default()
469        }
470    }
471
472    #[tokio::test]
473    async fn running_infinite_timer() {
474        let mut timer = testing_timer();
475
476        assert_eq!(timer.state, TimerState::Running);
477        assert_eq!(timer.cycle, TimerCycle::new("a", 3));
478
479        // next ticks: state should still be running, cycle name
480        // should be the same and cycle duration should be decremented
481        // by 2
482
483        MockClock::advance(Duration::from_secs(2));
484        timer.update().await;
485
486        assert_eq!(timer.state, TimerState::Running);
487        assert_eq!(timer.cycle, TimerCycle::new("a", 1));
488
489        // next tick: state should still be running, cycle should
490        // switch to the next one
491
492        MockClock::advance(Duration::from_secs(1));
493        timer.update().await;
494
495        assert_eq!(timer.state, TimerState::Running);
496        assert_eq!(timer.cycle, TimerCycle::new("b", 2));
497
498        // next ticks: state should still be running, cycle should
499        // switch to the next one
500
501        MockClock::advance(Duration::from_secs(2));
502        timer.update().await;
503
504        assert_eq!(timer.state, TimerState::Running);
505        assert_eq!(timer.cycle, TimerCycle::new("c", 1));
506
507        // next tick: state should still be running, cycle should
508        // switch back to the first one
509
510        MockClock::advance(Duration::from_secs(1));
511        timer.update().await;
512
513        assert_eq!(timer.state, TimerState::Running);
514        assert_eq!(timer.cycle, TimerCycle::new("a", 3));
515    }
516
517    #[tokio::test]
518    async fn running_timer_events() {
519        static EVENTS: Lazy<Mutex<Vec<TimerEvent>>> = Lazy::new(|| Mutex::const_new(Vec::new()));
520
521        let mut timer = testing_timer();
522
523        timer.config.handler = Arc::new(|evt| {
524            Box::pin(async {
525                EVENTS.lock().await.push(evt);
526                Ok(())
527            })
528        });
529
530        // from a3 to b1
531        MockClock::advance(Duration::from_secs(1));
532        timer.update().await;
533        MockClock::advance(Duration::from_secs(1));
534        timer.update().await;
535        MockClock::advance(Duration::from_secs(1));
536        timer.update().await;
537        MockClock::advance(Duration::from_secs(1));
538        timer.update().await;
539
540        assert_eq!(
541            *EVENTS.lock().await,
542            vec![
543                TimerEvent::Running(TimerCycle::new("a", 3)),
544                TimerEvent::Running(TimerCycle::new("a", 2)),
545                TimerEvent::Running(TimerCycle::new("a", 1)),
546                TimerEvent::Ended(TimerCycle::new("a", 0)),
547                TimerEvent::Began(TimerCycle::new("b", 2)),
548                TimerEvent::Running(TimerCycle::new("b", 2)),
549            ]
550        );
551    }
552
553    #[tokio::test]
554    async fn paused_timer_not_impacted_by_iterator() {
555        let mut timer = testing_timer();
556        timer.state = TimerState::Paused;
557        let prev_timer = timer.clone();
558        timer.update().await;
559        assert_eq!(prev_timer, timer);
560    }
561
562    #[tokio::test]
563    async fn stopped_timer_not_impacted_by_iterator() {
564        let mut timer = testing_timer();
565        timer.state = TimerState::Stopped;
566        let prev_timer = timer.clone();
567        timer.update().await;
568        assert_eq!(prev_timer, timer);
569    }
570
571    #[cfg(feature = "server")]
572    #[tokio::test]
573    async fn thread_safe_timer() {
574        let mut timer = testing_timer();
575        static EVENTS: Lazy<Mutex<Vec<TimerEvent>>> = Lazy::new(|| Mutex::const_new(Vec::new()));
576
577        timer.config.handler = Arc::new(move |evt| {
578            Box::pin(async {
579                EVENTS.lock().await.push(evt);
580                Ok(())
581            })
582        });
583        let timer = ThreadSafeTimer::new(timer.config).unwrap();
584
585        assert_eq!(
586            timer.get().await,
587            Timer {
588                state: TimerState::Stopped,
589                cycle: TimerCycle::new("a", 3),
590                ..Default::default()
591            }
592        );
593
594        timer.start().await.unwrap();
595        timer.set(21).await.unwrap();
596
597        assert_eq!(
598            timer.get().await,
599            Timer {
600                state: TimerState::Running,
601                cycle: TimerCycle::new("a", 21),
602                ..Default::default()
603            }
604        );
605
606        assert_eq!(
607            timer.get().await,
608            Timer {
609                state: TimerState::Running,
610                cycle: TimerCycle::new("a", 21),
611                ..Default::default()
612            }
613        );
614
615        timer.pause().await.unwrap();
616
617        assert_eq!(
618            timer.get().await,
619            Timer {
620                state: TimerState::Paused,
621                cycle: TimerCycle::new("a", 21),
622                ..Default::default()
623            }
624        );
625
626        timer.resume().await.unwrap();
627
628        assert_eq!(
629            timer.get().await,
630            Timer {
631                state: TimerState::Running,
632                cycle: TimerCycle::new("a", 21),
633                ..Default::default()
634            }
635        );
636
637        timer.stop().await.unwrap();
638
639        assert_eq!(
640            timer.get().await,
641            Timer {
642                state: TimerState::Stopped,
643                cycle: TimerCycle::new("a", 3),
644                ..Default::default()
645            }
646        );
647
648        assert_eq!(
649            *EVENTS.lock().await,
650            vec![
651                TimerEvent::Started,
652                TimerEvent::Began(TimerCycle::new("a", 3)),
653                TimerEvent::Set(TimerCycle::new("a", 21)),
654                TimerEvent::Paused(TimerCycle::new("a", 21)),
655                TimerEvent::Resumed(TimerCycle::new("a", 21)),
656                TimerEvent::Ended(TimerCycle::new("a", 21)),
657                TimerEvent::Stopped,
658            ]
659        );
660    }
661}