use crate::fraction::Fraction;
use crate::{
duration::{self, *},
fixed_point::FixedPoint,
timer::param::*,
ConversionError, Instant, TimeError,
};
use core::{convert::TryFrom, marker::PhantomData, ops::Add, prelude::v1::*};
pub(crate) mod param {
#[derive(Debug, Hash)]
pub struct None;
#[derive(Debug, Hash)]
pub struct Armed;
#[derive(Debug, Hash)]
pub struct Running;
#[derive(Debug, Hash)]
pub struct Periodic;
#[derive(Debug, Hash)]
pub struct OneShot;
}
#[derive(Debug, Hash)]
pub struct Timer<'a, Type, State, Clock: crate::Clock, Dur: Duration> {
clock: &'a Clock,
duration: Dur,
expiration: Instant<Clock>,
_type: PhantomData<Type>,
_state: PhantomData<State>,
}
impl<'a, Clock: crate::Clock, Dur: Duration> Timer<'_, param::None, param::None, Clock, Dur> {
#[allow(clippy::new_ret_no_self)]
pub fn new(clock: &Clock, duration: Dur) -> Timer<OneShot, Armed, Clock, Dur> {
Timer::<OneShot, Armed, Clock, Dur> {
clock,
duration,
expiration: Instant::new(Clock::T::from(0)),
_type: PhantomData,
_state: PhantomData,
}
}
}
impl<'a, Type, State, Clock: crate::Clock, Dur: Duration> Timer<'a, Type, State, Clock, Dur> {
pub fn into_oneshot(self) -> Timer<'a, OneShot, State, Clock, Dur> {
Timer::<OneShot, State, Clock, Dur> {
clock: self.clock,
duration: self.duration,
expiration: self.expiration,
_type: PhantomData,
_state: PhantomData,
}
}
pub fn into_periodic(self) -> Timer<'a, Periodic, State, Clock, Dur> {
Timer::<Periodic, State, Clock, Dur> {
clock: self.clock,
duration: self.duration,
expiration: self.expiration,
_type: PhantomData,
_state: PhantomData,
}
}
}
impl<'a, Type, Clock: crate::Clock, Dur: Duration> Timer<'a, Type, Armed, Clock, Dur> {
pub fn start(self) -> Result<Timer<'a, Type, Running, Clock, Dur>, TimeError>
where
Clock::T: TryFrom<Dur::T>,
Dur: FixedPoint,
{
Ok(Timer::<Type, Running, Clock, Dur> {
clock: self.clock,
duration: self.duration,
expiration: self
.clock
.try_now()?
.checked_add(self.duration)
.ok_or(ConversionError::Overflow)?,
_type: PhantomData,
_state: PhantomData,
})
}
}
impl<Type, Clock: crate::Clock, Dur: Duration> Timer<'_, Type, Running, Clock, Dur> {
fn _is_expired(&self) -> Result<bool, TimeError> {
Ok(self.clock.try_now()? >= self.expiration)
}
pub fn elapsed(&self) -> Result<Dur, TimeError>
where
Dur: FixedPoint + TryFrom<duration::Generic<Clock::T>, Error = ConversionError>,
Dur::T: TryFrom<Clock::T>,
Clock::T: TryFrom<Dur::T>,
{
let generic_duration = self
.clock
.try_now()?
.checked_duration_since(
&(self
.expiration
.checked_sub(self.duration)
.ok_or(ConversionError::Overflow)?),
)
.ok_or(TimeError::Overflow)?;
Ok(Dur::try_from(generic_duration)?)
}
pub fn remaining(&self) -> Result<Dur, TimeError>
where
Dur: FixedPoint + TryFrom<duration::Generic<Clock::T>, Error = ConversionError>,
Dur::T: TryFrom<u32> + TryFrom<Clock::T>,
Clock::T: TryFrom<Dur::T>,
{
let result = self
.expiration
.checked_duration_since(&self.clock.try_now()?)
.or_else(|| {
Some(duration::Generic::<Clock::T>::new(
0.into(),
Fraction::default(),
))
})
.ok_or(TimeError::NegDuration)?;
Ok(Dur::try_from(result)?)
}
}
impl<'a, Clock: crate::Clock, Dur: Duration> Timer<'a, OneShot, Running, Clock, Dur> {
pub fn wait(self) -> Result<Timer<'a, OneShot, Armed, Clock, Dur>, TimeError> {
while !self._is_expired()? {}
Ok(Timer::<param::None, param::None, Clock, Dur>::new(
self.clock,
self.duration,
))
}
pub fn is_expired(&self) -> Result<bool, TimeError> {
self._is_expired()
}
}
impl<Clock: crate::Clock, Dur: Duration> Timer<'_, Periodic, Running, Clock, Dur> {
pub fn wait(self) -> Result<Self, TimeError>
where
Instant<Clock>: Add<Dur, Output = Instant<Clock>>,
{
while !self._is_expired()? {}
Ok(Self {
clock: self.clock,
duration: self.duration,
expiration: self.expiration + self.duration,
_type: PhantomData,
_state: PhantomData,
})
}
pub fn period_complete(&mut self) -> Result<bool, TimeError>
where
Instant<Clock>: Add<Dur, Output = Instant<Clock>>,
{
if self._is_expired()? {
self.expiration = self.expiration + self.duration;
Ok(true)
} else {
Ok(false)
}
}
}
#[cfg(test)]
mod test {}