use std::cmp::Ordering;
use try_guard::guard;
use crate::time::TimeGenerator;
use crate::window::MappedWindow;
pub struct RandomAccessScheduler<'a, G> where G: TimeGenerator {
time_gen: G,
windows: Vec<MappedWindow<'a, G::Time>>,
interrupt: Option<Box<FnMut(G::Time) -> Interrupt + 'a>>
}
impl<'a, G> RandomAccessScheduler<'a, G> where G: TimeGenerator {
pub fn new<W>(
time_gen: G,
windows: W
) -> Option<Self>
where W: Into<Vec<MappedWindow<'a, G::Time>>> {
let mut windows = windows.into();
windows.sort_by(|a, b| a.window.start.partial_cmp(&b.window.start).unwrap_or(Ordering::Less));
let overlapping = windows.iter().zip(windows.iter().skip(1)).any(|(a, b)| {
b.window.start < a.window.end
});
guard!(!overlapping);
Some(RandomAccessScheduler {
time_gen,
windows,
interrupt: None
})
}
fn active_window_index(&self, t: G::Time) -> Option<usize> {
self.windows.binary_search_by(|win| {
match win.window.start.partial_cmp(&t).unwrap_or(Ordering::Less) {
Ordering::Equal => Ordering::Equal,
Ordering::Greater => Ordering::Greater,
Ordering::Less => match t.partial_cmp(&win.window.end).unwrap_or(Ordering::Less) {
Ordering::Less | Ordering::Equal => Ordering::Equal,
Ordering::Greater => Ordering::Less
}
}
}).ok()
}
pub fn schedule(&mut self) {
self.time_gen.reset();
let mut t = self.time_gen.current();
loop {
if let Some(ref mut interrupt) = self.interrupt {
if let Interrupt::Break = (interrupt)(t) {
break;
}
}
let win_ix = self.active_window_index(t);
if let Some(win_ix) = win_ix {
((&mut self.windows[win_ix]).carry)(t);
}
self.time_gen.tick();
t = self.time_gen.current();
if let Some(last_win) = self.windows.last() {
if t >= last_win.window.end {
break
}
}
}
}
pub fn interruptible_with<F>(&mut self, interrupt: F) where F: FnMut(G::Time) -> Interrupt + 'a {
self.interrupt = Some(Box::new(interrupt));
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Interrupt {
Break,
Continue
}