#[cfg(all(feature = "server", test))]
use mock_instant::Instant;
use serde::{Deserialize, Serialize};
#[cfg(feature = "server")]
use std::io::{Error, ErrorKind};
#[cfg(all(feature = "server", not(test)))]
use std::time::Instant;
use std::{
fmt,
io::Result,
ops::{Deref, DerefMut},
sync::Arc,
};
#[cfg(feature = "server")]
use tokio::sync::Mutex;
use crate::handler::{self, Handler};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub enum TimerLoop {
#[default]
Infinite,
Fixed(usize),
}
impl From<usize> for TimerLoop {
fn from(count: usize) -> Self {
if count == 0 {
Self::Infinite
} else {
Self::Fixed(count)
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct TimerCycle {
pub name: String,
pub duration: usize,
}
impl TimerCycle {
pub fn new(name: impl ToString, duration: usize) -> Self {
Self {
name: name.to_string(),
duration,
}
}
}
impl<T: ToString> From<(T, usize)> for TimerCycle {
fn from((name, duration): (T, usize)) -> Self {
Self::new(name, duration)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct TimerCycles(Vec<TimerCycle>);
impl<T: IntoIterator<Item = TimerCycle>> From<T> for TimerCycles {
fn from(cycles: T) -> Self {
Self(cycles.into_iter().collect())
}
}
impl Deref for TimerCycles {
type Target = Vec<TimerCycle>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for TimerCycles {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub enum TimerState {
Running,
Paused,
#[default]
Stopped,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TimerEvent {
Started,
Began(TimerCycle),
Running(TimerCycle),
Set(TimerCycle),
Paused(TimerCycle),
Resumed(TimerCycle),
Ended(TimerCycle),
Stopped,
}
#[derive(Clone)]
pub struct TimerConfig {
pub cycles: TimerCycles,
pub cycles_count: TimerLoop,
pub handler: Arc<Handler<TimerEvent>>,
}
impl Default for TimerConfig {
fn default() -> Self {
Self {
cycles: Default::default(),
cycles_count: Default::default(),
handler: handler::default(),
}
}
}
#[cfg(feature = "server")]
impl TimerConfig {
fn clone_first_cycle(&self) -> Result<TimerCycle> {
self.cycles.first().cloned().ok_or_else(|| {
Error::new(
ErrorKind::NotFound,
"cannot find first cycle from timer config",
)
})
}
}
#[derive(Clone, Default, Serialize, Deserialize)]
pub struct Timer {
#[serde(skip)]
pub config: TimerConfig,
pub state: TimerState,
pub cycle: TimerCycle,
pub cycles_count: TimerLoop,
#[cfg(feature = "server")]
#[serde(skip)]
pub started_at: Option<Instant>,
#[cfg(feature = "server")]
pub elapsed: usize,
}
impl fmt::Debug for Timer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let timer = serde_json::to_string(self).map_err(|_| fmt::Error)?;
write!(f, "{timer}")
}
}
impl Eq for Timer {}
#[cfg(feature = "server")]
impl PartialEq for Timer {
fn eq(&self, other: &Self) -> bool {
self.state == other.state && self.cycle == other.cycle && self.elapsed() == other.elapsed()
}
}
#[cfg(not(feature = "server"))]
impl PartialEq for Timer {
fn eq(&self, other: &Self) -> bool {
self.state == other.state && self.cycle == other.cycle
}
}
#[cfg(feature = "server")]
impl Timer {
pub fn elapsed(&self) -> usize {
self.started_at
.map(|i| i.elapsed().as_secs() as usize)
.unwrap_or_default()
+ self.elapsed
}
pub async fn update(&mut self) {
let mut elapsed = self.elapsed();
match self.state {
TimerState::Running => {
let (cycles, total_duration) = self.config.cycles.iter().cloned().fold(
(Vec::new(), 0),
|(mut cycles, mut sum), mut cycle| {
cycle.duration += sum;
sum = cycle.duration;
cycles.push(cycle);
(cycles, sum)
},
);
if let TimerLoop::Fixed(cycles_count) = self.cycles_count {
if elapsed >= (total_duration * cycles_count) {
self.state = TimerState::Stopped;
return;
}
}
elapsed = elapsed % total_duration;
let last_cycle = cycles[cycles.len() - 1].clone();
let next_cycle = cycles
.into_iter()
.fold(None, |next_cycle, mut cycle| match next_cycle {
None if elapsed < cycle.duration => {
cycle.duration = cycle.duration - elapsed;
Some(cycle)
}
_ => next_cycle,
})
.unwrap_or(last_cycle);
self.fire_event(TimerEvent::Running(self.cycle.clone()))
.await;
if self.cycle.name != next_cycle.name {
let mut prev_cycle = self.cycle.clone();
prev_cycle.duration = 0;
self.fire_events([
TimerEvent::Ended(prev_cycle),
TimerEvent::Began(next_cycle.clone()),
])
.await;
}
self.cycle = next_cycle;
}
TimerState::Paused => {
}
TimerState::Stopped => {
}
}
}
pub async fn fire_event(&self, event: TimerEvent) {
let handler = &self.config.handler;
if let Err(err) = handler(event.clone()).await {
log::debug!("cannot fire event {event:?}");
log::debug!("{err:?}");
}
}
pub async fn fire_events(&self, events: impl IntoIterator<Item = TimerEvent>) {
for event in events.into_iter() {
self.fire_event(event).await
}
}
pub async fn start(&mut self) -> Result<()> {
if matches!(self.state, TimerState::Stopped) {
self.state = TimerState::Running;
self.cycle = self.config.clone_first_cycle()?;
self.cycles_count = self.config.cycles_count.clone();
self.started_at = Some(Instant::now());
self.elapsed = 0;
self.fire_events([TimerEvent::Started, TimerEvent::Began(self.cycle.clone())])
.await;
}
Ok(())
}
pub async fn set(&mut self, duration: usize) -> Result<()> {
self.cycle.duration = duration;
self.fire_event(TimerEvent::Set(self.cycle.clone())).await;
Ok(())
}
pub async fn pause(&mut self) -> Result<()> {
if matches!(self.state, TimerState::Running) {
self.state = TimerState::Paused;
self.elapsed = self.elapsed();
self.started_at = None;
self.fire_event(TimerEvent::Paused(self.cycle.clone()))
.await;
}
Ok(())
}
pub async fn resume(&mut self) -> Result<()> {
if matches!(self.state, TimerState::Paused) {
self.state = TimerState::Running;
self.started_at = Some(Instant::now());
self.fire_event(TimerEvent::Resumed(self.cycle.clone()))
.await;
}
Ok(())
}
pub async fn stop(&mut self) -> Result<()> {
if matches!(self.state, TimerState::Running) {
self.state = TimerState::Stopped;
self.fire_events([TimerEvent::Ended(self.cycle.clone()), TimerEvent::Stopped])
.await;
self.cycle = self.config.clone_first_cycle()?;
self.cycles_count = self.config.cycles_count.clone();
self.started_at = None;
self.elapsed = 0;
}
Ok(())
}
}
#[cfg(feature = "server")]
#[derive(Clone, Debug, Default)]
pub struct ThreadSafeTimer(Arc<Mutex<Timer>>);
#[cfg(feature = "server")]
impl ThreadSafeTimer {
pub fn new(config: TimerConfig) -> Result<Self> {
let mut timer = Timer::default();
timer.config = config;
timer.cycle = timer.config.clone_first_cycle()?;
timer.cycles_count = timer.config.cycles_count.clone();
Ok(Self(Arc::new(Mutex::new(timer))))
}
pub async fn update(&self) {
self.0.lock().await.update().await;
}
pub async fn start(&self) -> Result<()> {
self.0.lock().await.start().await
}
pub async fn get(&self) -> Timer {
self.0.lock().await.clone()
}
pub async fn set(&self, duration: usize) -> Result<()> {
self.0.lock().await.set(duration).await
}
pub async fn pause(&self) -> Result<()> {
self.0.lock().await.pause().await
}
pub async fn resume(&self) -> Result<()> {
self.0.lock().await.resume().await
}
pub async fn stop(&self) -> Result<()> {
self.0.lock().await.stop().await
}
}
#[cfg(feature = "server")]
impl Deref for ThreadSafeTimer {
type Target = Arc<Mutex<Timer>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(feature = "server")]
impl DerefMut for ThreadSafeTimer {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[cfg(test)]
mod tests {
use mock_instant::{Instant, MockClock};
use once_cell::sync::Lazy;
use std::{sync::Arc, time::Duration};
use super::*;
fn testing_timer() -> Timer {
Timer {
config: TimerConfig {
cycles: TimerCycles::from([
TimerCycle::new("a", 3),
TimerCycle::new("b", 2),
TimerCycle::new("c", 1),
]),
..Default::default()
},
state: TimerState::Running,
cycle: TimerCycle::new("a", 3),
started_at: Some(Instant::now()),
..Default::default()
}
}
#[tokio::test]
async fn running_infinite_timer() {
let mut timer = testing_timer();
assert_eq!(timer.state, TimerState::Running);
assert_eq!(timer.cycle, TimerCycle::new("a", 3));
MockClock::advance(Duration::from_secs(2));
timer.update().await;
assert_eq!(timer.state, TimerState::Running);
assert_eq!(timer.cycle, TimerCycle::new("a", 1));
MockClock::advance(Duration::from_secs(1));
timer.update().await;
assert_eq!(timer.state, TimerState::Running);
assert_eq!(timer.cycle, TimerCycle::new("b", 2));
MockClock::advance(Duration::from_secs(2));
timer.update().await;
assert_eq!(timer.state, TimerState::Running);
assert_eq!(timer.cycle, TimerCycle::new("c", 1));
MockClock::advance(Duration::from_secs(1));
timer.update().await;
assert_eq!(timer.state, TimerState::Running);
assert_eq!(timer.cycle, TimerCycle::new("a", 3));
}
#[tokio::test]
async fn running_timer_events() {
static EVENTS: Lazy<Mutex<Vec<TimerEvent>>> = Lazy::new(|| Mutex::const_new(Vec::new()));
let mut timer = testing_timer();
timer.config.handler = Arc::new(|evt| {
Box::pin(async {
EVENTS.lock().await.push(evt);
Ok(())
})
});
MockClock::advance(Duration::from_secs(1));
timer.update().await;
MockClock::advance(Duration::from_secs(1));
timer.update().await;
MockClock::advance(Duration::from_secs(1));
timer.update().await;
MockClock::advance(Duration::from_secs(1));
timer.update().await;
assert_eq!(
*EVENTS.lock().await,
vec![
TimerEvent::Running(TimerCycle::new("a", 3)),
TimerEvent::Running(TimerCycle::new("a", 2)),
TimerEvent::Running(TimerCycle::new("a", 1)),
TimerEvent::Ended(TimerCycle::new("a", 0)),
TimerEvent::Began(TimerCycle::new("b", 2)),
TimerEvent::Running(TimerCycle::new("b", 2)),
]
);
}
#[tokio::test]
async fn paused_timer_not_impacted_by_iterator() {
let mut timer = testing_timer();
timer.state = TimerState::Paused;
let prev_timer = timer.clone();
timer.update().await;
assert_eq!(prev_timer, timer);
}
#[tokio::test]
async fn stopped_timer_not_impacted_by_iterator() {
let mut timer = testing_timer();
timer.state = TimerState::Stopped;
let prev_timer = timer.clone();
timer.update().await;
assert_eq!(prev_timer, timer);
}
#[cfg(feature = "server")]
#[tokio::test]
async fn thread_safe_timer() {
let mut timer = testing_timer();
static EVENTS: Lazy<Mutex<Vec<TimerEvent>>> = Lazy::new(|| Mutex::const_new(Vec::new()));
timer.config.handler = Arc::new(move |evt| {
Box::pin(async {
EVENTS.lock().await.push(evt);
Ok(())
})
});
let timer = ThreadSafeTimer::new(timer.config).unwrap();
assert_eq!(
timer.get().await,
Timer {
state: TimerState::Stopped,
cycle: TimerCycle::new("a", 3),
..Default::default()
}
);
timer.start().await.unwrap();
timer.set(21).await.unwrap();
assert_eq!(
timer.get().await,
Timer {
state: TimerState::Running,
cycle: TimerCycle::new("a", 21),
..Default::default()
}
);
assert_eq!(
timer.get().await,
Timer {
state: TimerState::Running,
cycle: TimerCycle::new("a", 21),
..Default::default()
}
);
timer.pause().await.unwrap();
assert_eq!(
timer.get().await,
Timer {
state: TimerState::Paused,
cycle: TimerCycle::new("a", 21),
..Default::default()
}
);
timer.resume().await.unwrap();
assert_eq!(
timer.get().await,
Timer {
state: TimerState::Running,
cycle: TimerCycle::new("a", 21),
..Default::default()
}
);
timer.stop().await.unwrap();
assert_eq!(
timer.get().await,
Timer {
state: TimerState::Stopped,
cycle: TimerCycle::new("a", 3),
..Default::default()
}
);
assert_eq!(
*EVENTS.lock().await,
vec![
TimerEvent::Started,
TimerEvent::Began(TimerCycle::new("a", 3)),
TimerEvent::Set(TimerCycle::new("a", 21)),
TimerEvent::Paused(TimerCycle::new("a", 21)),
TimerEvent::Resumed(TimerCycle::new("a", 21)),
TimerEvent::Ended(TimerCycle::new("a", 21)),
TimerEvent::Stopped,
]
);
}
}