use std::any::Any;
use std::cell::Cell;
use std::cmp::{Ordering, Reverse};
use std::collections::BinaryHeap;
use std::fmt;
use std::rc::Rc;
use std::time::Duration;
use crate::{Clock, ComponentId};
#[derive(Debug)]
pub struct EventEntry {
time: Reverse<Duration>,
component: usize,
inner: Box<dyn Any>,
}
impl EventEntry {
pub(crate) fn new<E: fmt::Debug + 'static>(
time: Duration,
component: ComponentId<E>,
event: E,
) -> Self {
EventEntry {
time: Reverse(time),
component: component.id,
inner: Box::new(event),
}
}
#[must_use]
pub(crate) fn downcast<E: fmt::Debug + 'static>(&self) -> Option<EventEntryTyped<'_, E>> {
self.inner.downcast_ref::<E>().map(|event| EventEntryTyped {
time: self.time.0,
component_id: ComponentId::new(self.component),
component_idx: self.component,
event,
})
}
#[must_use]
pub(crate) fn component_idx(&self) -> usize {
self.component
}
}
impl PartialEq for EventEntry {
fn eq(&self, other: &Self) -> bool {
self.time == other.time
}
}
impl Eq for EventEntry {}
impl PartialOrd for EventEntry {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.time.partial_cmp(&other.time)
}
}
impl Ord for EventEntry {
fn cmp(&self, other: &Self) -> Ordering {
self.time.cmp(&other.time)
}
}
#[derive(Debug)]
pub struct EventEntryTyped<'e, E: fmt::Debug> {
pub time: Duration,
pub component_id: ComponentId<E>,
pub component_idx: usize,
pub event: &'e E,
}
pub struct ClockRef {
clock: Clock,
}
impl From<Clock> for ClockRef {
fn from(clock: Clock) -> Self {
Self { clock }
}
}
impl ClockRef {
#[must_use]
pub fn time(&self) -> Duration {
self.clock.get()
}
}
pub struct Scheduler {
events: BinaryHeap<EventEntry>,
clock: Clock,
}
impl Default for Scheduler {
fn default() -> Self {
Self {
events: BinaryHeap::default(),
clock: Rc::new(Cell::new(Duration::default())),
}
}
}
impl Scheduler {
pub fn schedule<E: fmt::Debug + 'static>(
&mut self,
time: Duration,
component: ComponentId<E>,
event: E,
) {
let time = self.time() + time;
self.events.push(EventEntry::new(time, component, event));
}
pub fn schedule_now<E: fmt::Debug + 'static>(&mut self, component: ComponentId<E>, event: E) {
self.schedule(Duration::default(), component, event);
}
#[must_use]
pub fn time(&self) -> Duration {
self.clock.get()
}
#[must_use]
pub fn clock(&self) -> ClockRef {
ClockRef {
clock: Rc::clone(&self.clock),
}
}
pub fn pop(&mut self) -> Option<EventEntry> {
self.events.pop().map(|event| {
self.clock.replace(event.time.0);
event
})
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_clock_ref() {
let time = Duration::from_secs(1);
let clock = Clock::new(Cell::new(time));
let clock_ref = ClockRef::from(clock);
assert_eq!(clock_ref.time(), time);
}
#[test]
fn test_event_entry_debug() {
let entry = EventEntry {
time: Reverse(Duration::from_secs(1)),
component: 2,
inner: Box::new(String::from("inner")),
};
assert_eq!(
&format!("{:?}", entry),
"EventEntry { time: Reverse(1s), component: 2, inner: Any }"
);
let typed = entry.downcast::<String>().unwrap();
assert_eq!(
&format!("{:?}", typed),
"EventEntryTyped { time: 1s, component_id: ComponentId { id: 2, _marker: PhantomData }, component_idx: 2, event: \"inner\" }"
);
}
#[test]
fn test_event_entry_downcast() {
let entry = EventEntry {
time: Reverse(Duration::from_secs(1)),
component: 2,
inner: Box::new(String::from("inner")),
};
assert!(entry.downcast::<String>().is_some());
assert!(entry.downcast::<i32>().is_none());
}
#[test]
fn test_event_entry_cmp() {
let make_entry = || EventEntry {
time: Reverse(Duration::from_secs(1)),
component: 2,
inner: Box::new(String::from("inner")),
};
assert_eq!(
EventEntry {
time: Reverse(Duration::from_secs(1)),
..make_entry()
},
EventEntry {
time: Reverse(Duration::from_secs(1)),
..make_entry()
}
);
assert_eq!(
EventEntry {
time: Reverse(Duration::from_secs(0)),
..make_entry()
}
.cmp(&EventEntry {
time: Reverse(Duration::from_secs(1)),
..make_entry()
}),
Ordering::Greater
);
assert_eq!(
EventEntry {
time: Reverse(Duration::from_secs(2)),
..make_entry()
}
.cmp(&EventEntry {
time: Reverse(Duration::from_secs(1)),
..make_entry()
}),
Ordering::Less
);
}
#[derive(Debug, Clone, Eq, PartialEq)]
struct EventA;
#[derive(Debug, Clone, Eq, PartialEq)]
struct EventB;
#[test]
fn test_scheduler() {
let mut scheduler = Scheduler::default();
assert_eq!(scheduler.time(), Duration::new(0, 0));
assert_eq!(scheduler.clock().time(), Duration::new(0, 0));
assert!(scheduler.events.is_empty());
let component_a = ComponentId::<EventA>::new(0);
let component_b = ComponentId::<EventB>::new(1);
scheduler.schedule(Duration::from_secs(1), component_a, EventA);
scheduler.schedule_now(component_b, EventB);
scheduler.schedule(Duration::from_secs(2), component_b, EventB);
assert_eq!(scheduler.time(), Duration::from_secs(0));
let entry = scheduler.pop().unwrap();
let entry = entry.downcast::<EventB>().unwrap();
assert_eq!(entry.time, Duration::from_secs(0));
assert_eq!(entry.component_idx, 1);
assert_eq!(entry.component_id, component_b);
assert_eq!(entry.event, &EventB);
assert_eq!(scheduler.time(), Duration::from_secs(0));
let entry = scheduler.pop().unwrap();
let entry = entry.downcast::<EventA>().unwrap();
assert_eq!(entry.time, Duration::from_secs(1));
assert_eq!(entry.component_idx, 0);
assert_eq!(entry.component_id, component_a);
assert_eq!(entry.event, &EventA);
assert_eq!(scheduler.time(), Duration::from_secs(1));
assert_eq!(scheduler.clock().time(), Duration::from_secs(1));
let entry = scheduler.pop().unwrap();
let entry = entry.downcast::<EventB>().unwrap();
assert_eq!(entry.time, Duration::from_secs(2));
assert_eq!(entry.component_idx, 1);
assert_eq!(entry.component_id, component_b);
assert_eq!(entry.event, &EventB);
assert_eq!(scheduler.time(), Duration::from_secs(2));
assert!(scheduler.pop().is_none());
}
}