use strafesnet_common::integer::{Time,Ratio64}; pub trait TimerState:Copy{ fn get_time(&self,time:Time)->Time; fn set_time(&mut self,time:Time,new_time:Time); fn get_offset(&self)->Time; fn set_offset(&mut self,offset:Time); } #[derive(Clone,Copy,Debug)] struct Scaled{ scale:Ratio64, offset:Time, } impl Scaled{ fn scale(&self,time:Time)->Time{ Time::raw(self.scale.mul_int(time.get())) } fn get_scale(&self)->Ratio64{ self.scale } fn set_scale(&mut self,time:Time,new_scale:Ratio64){ let new_time=self.get_time(time); self.scale=new_scale; self.set_time(time,new_time); } } impl TimerState for Scaled{ fn get_time(&self,time:Time)->Time{ self.scale(time)+self.offset } fn set_time(&mut self,time:Time,new_time:Time){ self.offset=new_time-self.scale(time); } fn get_offset(&self)->Time{ self.offset } fn set_offset(&mut self,offset:Time){ self.offset=offset; } } #[derive(Clone,Copy,Debug)] struct Realtime{ offset:Time, } impl TimerState for Realtime{ fn get_time(&self,time:Time)->Time{ time+self.offset } fn set_time(&mut self,time:Time,new_time:Time){ self.offset=new_time-time; } fn get_offset(&self)->Time{ self.offset } fn set_offset(&mut self,offset:Time){ self.offset=offset; } } #[derive(Clone,Debug)] pub struct Timer{ state:T, paused:bool, } impl Timer{ pub fn realtime(offset:Time)->Self{ Self{ state:Realtime{offset}, paused:false, } } pub fn realtime_paused(offset:Time)->Self{ Self{ state:Realtime{offset}, paused:true, } } } #[derive(Debug)] pub enum Error{ AlreadyPaused, AlreadyUnpaused, } impl std::fmt::Display for Error{ fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ write!(f,"{self:?}") } } impl std::error::Error for Error{} impl Timer{ pub fn scaled(scale:Ratio64,offset:Time)->Self{ Self{ state:Scaled{scale,offset}, paused:false, } } pub fn scaled_paused(scale:Ratio64,offset:Time)->Self{ Self{ state:Scaled{scale,offset}, paused:true, } } pub fn get_scale(&mut self)->Ratio64{ self.state.get_scale() } pub fn set_scale(&mut self,time:Time,new_scale:Ratio64){ self.state.set_scale(time,new_scale) } } impl Timer{ pub fn time(&self,time:Time)->Time{ match self.paused{ true=>self.state.get_offset(), false=>self.state.get_time(time), } } pub fn set_time(&mut self,time:Time,new_time:Time){ match self.paused{ true=>self.state.set_offset(new_time), false=>self.state.set_time(time,new_time), } } pub fn pause(&mut self,time:Time)->Result<(),Error>{ match self.paused{ true=>Err(Error::AlreadyPaused), false=>{ let new_time=self.time(time); self.state.set_offset(new_time); self.paused=true; Ok(()) }, } } pub fn unpause(&mut self,time:Time)->Result<(),Error>{ match self.paused{ true=>{ let new_time=self.time(time); self.state.set_time(time,new_time); self.paused=false; Ok(()) }, false=>Err(Error::AlreadyUnpaused), } } } #[cfg(test)] mod test{ use super::{Time,Timer,Error}; macro_rules! sec { ($s: expr) => { Time::from_secs($s) }; } #[test] fn test_timer()->Result<(),Error>{ //create a paused timer that reads 0s let mut timer=Timer::realtime_paused(sec!(0)); //the paused timer at 1 second should read 0s assert_eq!(timer.time(sec!(1)),sec!(0)); //unpause it after one second timer.unpause(sec!(1))?; //the timer at 6 seconds should read 5s assert_eq!(timer.time(sec!(6)),sec!(5)); //pause the timer after 11 seconds timer.pause(sec!(11))?; //the paused timer at 20 seconds should read 10s assert_eq!(timer.time(sec!(20)),sec!(10)); Ok(()) } }