use crate::clock::Clock;
use crate::instant::Instant;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum TimerError {
NotRunning,
Overflow,
}
#[derive(Debug, Clone, Copy)]
struct Timing<I> {
start_time: I,
expiration_time: I,
}
#[derive(Debug)]
pub struct Timer<'a, CLOCK>
where
CLOCK: Clock,
{
clock: &'a CLOCK,
timing: Option<Timing<CLOCK::Instant>>,
}
impl<'a, CLOCK> Timer<'a, CLOCK>
where
CLOCK: Clock,
{
pub fn new(clock: &'a CLOCK) -> Self {
Self {
clock,
timing: None,
}
}
pub fn duration(&self) -> Result<core::time::Duration, TimerError> {
if let Some(t) = &self.timing {
Ok(t.expiration_time.duration_since(t.start_time))
} else {
Err(TimerError::NotRunning)
}
}
pub fn is_running(&self) -> Result<bool, TimerError> {
if self.timing.is_some() {
self.is_expired().map(|expired| !expired)
} else {
Ok(false)
}
}
pub fn is_expired(&self) -> Result<bool, TimerError> {
if let Some(timing) = &self.timing {
let expired = self.clock.now() >= timing.expiration_time;
Ok(expired)
} else {
Err(TimerError::NotRunning)
}
}
pub fn duration_left(&self) -> Result<core::time::Duration, TimerError> {
if let Some(t) = &self.timing {
Ok(t.expiration_time.duration_since(self.clock.now()))
} else {
Err(TimerError::NotRunning)
}
}
pub fn try_start(&mut self, duration: core::time::Duration) -> Result<(), TimerError> {
let start_time = self.clock.now();
let expiration_time = start_time
.checked_add(duration)
.ok_or(TimerError::Overflow)?;
self.timing = Some(Timing {
start_time,
expiration_time,
});
Ok(())
}
pub fn try_wait(&mut self) -> nb::Result<(), TimerError> {
if self.is_expired()? {
self.timing = None;
Ok(())
} else {
Err(nb::Error::WouldBlock)
}
}
pub fn start(&mut self, duration: core::time::Duration) {
self.try_start(duration).expect("Could not start timer!");
}
pub fn wait(&mut self) -> nb::Result<(), void::Void> {
match self.try_wait() {
Err(nb::Error::Other(_)) => {
panic!("Error during wait for timer, probably not running!")
}
Ok(()) => Ok(()),
Err(nb::Error::WouldBlock) => Err(nb::Error::WouldBlock),
}
}
pub fn cancel(&mut self) -> Result<(), TimerError> {
if self.timing.is_some() && !self.is_expired()? {
self.timing = None;
Ok(())
} else {
self.timing = None;
Err(TimerError::NotRunning)
}
}
}
#[cfg(test)]
mod tests {
use mockall::predicate::*;
use mockall::*;
use crate::clock::Clock;
use crate::instant::Instant32;
mock!(
MyClock {
}
impl Clock for MyClock {
type Instant = Instant32<1000>;
fn now(&self) -> Instant32<1000>;
}
impl core::fmt::Debug for MyClock {
fn fmt<'a>(&self, f: &mut core::fmt::Formatter<'a>) -> Result<(), core::fmt::Error> {
write!(f, "Clock Debug")
}
}
);
#[test]
fn test_clock_mock() {
let mut mymock = MockMyClock::new();
mymock
.expect_now()
.once()
.return_once(move || Instant32::new(23));
assert!(mymock.now() == Instant32::new(23));
}
#[test]
fn test_timer_basic() {
let mut clock = MockMyClock::new();
clock
.expect_now()
.times(5)
.returning(move || Instant32::new(0));
clock
.expect_now()
.times(2)
.returning(move || Instant32::new(10));
clock
.expect_now()
.times(5)
.returning(|| Instant32::new(1000));
clock
.expect_now()
.times(2)
.returning(move || Instant32::new(2001));
let mut timer: super::Timer<'_, _> = super::Timer::new(&clock);
assert_eq!(timer.is_running(), Ok(false));
assert_eq!(timer.duration(), Err(super::TimerError::NotRunning));
assert_eq!(
timer.try_wait(),
Err(nb::Error::Other(super::TimerError::NotRunning))
);
timer.start(core::time::Duration::from_millis(10));
assert_eq!(timer.is_running(), Ok(true));
assert_eq!(timer.duration(), Ok(core::time::Duration::from_millis(10)));
assert_eq!(
timer.duration_left(),
Ok(core::time::Duration::from_millis(10))
);
assert_eq!(timer.is_expired(), Ok(false)); assert_eq!(timer.wait(), Err(nb::Error::WouldBlock)); assert_eq!(timer.is_expired(), Ok(true)); assert_eq!(timer.wait(), Ok(())); assert_eq!(timer.is_running(), Ok(false));
timer.start(core::time::Duration::from_secs(1));
assert_eq!(timer.is_running(), Ok(true));
assert_eq!(
timer.duration(),
Ok(core::time::Duration::from_millis(1000))
);
assert_eq!(
timer.duration_left(),
Ok(core::time::Duration::from_millis(1000))
);
assert_eq!(timer.is_expired(), Ok(false)); assert_eq!(timer.wait(), Err(nb::Error::WouldBlock)); assert_eq!(timer.is_expired(), Ok(true)); assert_eq!(timer.wait(), Ok(())); assert_eq!(timer.is_running(), Ok(false)); }
#[test]
fn test_timer_cancel() {
let mut clock = MockMyClock::new();
clock
.expect_now()
.times(3)
.returning(move || Instant32::new(0));
let mut timer: super::Timer<'_, _> = super::Timer::new(&clock);
assert_eq!(timer.cancel(), Err(super::TimerError::NotRunning));
timer.start(core::time::Duration::from_millis(10));
assert_eq!(timer.is_running(), Ok(true));
assert_eq!(timer.cancel(), Ok(()));
assert_eq!(timer.is_running(), Ok(false));
assert_eq!(timer.is_expired(), Err(super::TimerError::NotRunning));
assert_eq!(timer.cancel(), Err(super::TimerError::NotRunning));
assert_eq!(timer.duration(), Err(super::TimerError::NotRunning));
assert_eq!(timer.duration_left(), Err(super::TimerError::NotRunning));
}
}