use prometeu_hal::FrameId; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum FrameAuthorizationError { GameFrameAlreadyActive { active_frame_id: FrameId }, } #[derive(Debug, Clone, Default)] pub struct FrameScheduler { next_game_frame_id: FrameId, active_game_frame_id: Option, completed_game_frames: u64, logical_frame_overruns: u64, } impl FrameScheduler { pub fn authorize_game_frame(&mut self) -> Result { if let Some(active_frame_id) = self.active_game_frame_id { return Err(FrameAuthorizationError::GameFrameAlreadyActive { active_frame_id }); } let frame_id = self.next_game_frame_id; self.next_game_frame_id = self.next_game_frame_id.next(); self.active_game_frame_id = Some(frame_id); Ok(frame_id) } pub fn complete_game_frame(&mut self) -> Option { let completed = self.active_game_frame_id.take(); if completed.is_some() { self.completed_game_frames = self.completed_game_frames.wrapping_add(1); } completed } pub fn record_logical_frame_overrun(&mut self) { self.logical_frame_overruns = self.logical_frame_overruns.wrapping_add(1); } pub fn active_game_frame_id(&self) -> Option { self.active_game_frame_id } pub fn completed_game_frames(&self) -> u64 { self.completed_game_frames } pub fn logical_frame_overruns(&self) -> u64 { self.logical_frame_overruns } } #[cfg(test)] mod tests { use super::*; #[test] fn authorizes_one_game_frame_until_completion() { let mut scheduler = FrameScheduler::default(); assert_eq!(scheduler.authorize_game_frame(), Ok(FrameId::ZERO)); assert_eq!( scheduler.authorize_game_frame(), Err(FrameAuthorizationError::GameFrameAlreadyActive { active_frame_id: FrameId::ZERO }) ); assert_eq!(scheduler.complete_game_frame(), Some(FrameId::ZERO)); assert_eq!(scheduler.authorize_game_frame(), Ok(FrameId::new(1))); } #[test] fn completion_is_sequential_without_catch_up_frames() { let mut scheduler = FrameScheduler::default(); for expected in 0..3 { let frame_id = scheduler.authorize_game_frame().expect("frame authorization"); assert_eq!(frame_id, FrameId::new(expected)); assert_eq!(scheduler.complete_game_frame(), Some(FrameId::new(expected))); } assert_eq!(scheduler.completed_game_frames(), 3); } #[test] fn records_logical_frame_overrun_without_advancing_frame_id() { let mut scheduler = FrameScheduler::default(); assert_eq!(scheduler.authorize_game_frame(), Ok(FrameId::ZERO)); scheduler.record_logical_frame_overrun(); scheduler.record_logical_frame_overrun(); assert_eq!(scheduler.logical_frame_overruns(), 2); assert_eq!(scheduler.active_game_frame_id(), Some(FrameId::ZERO)); assert_eq!(scheduler.complete_game_frame(), Some(FrameId::ZERO)); assert_eq!(scheduler.authorize_game_frame(), Ok(FrameId::new(1))); } }