use crate::{Attending, AttentionAlg, ClockAlg, RunHoldAlg, RunReadAlg, UndertakingAlg, Wanted};
use alux_ext::ext;
use std::time::Duration;
#[ext(name = SessionExt)]
pub impl<This> This
where
This: UndertakingAlg + RunReadAlg + RunHoldAlg + AttentionAlg + ClockAlg,
{
fn attend(&mut self, running: &mut Vec<Attending<This::Id, This::Run, This::Moment>>) {
let mut index = 0;
while index < running.len() {
if self.attend_one(&mut running[index]) {
index += 1;
continue;
}
let attending = running.swap_remove(index);
let ending = self.end_run(attending.run);
self.ended(&attending.id, ending);
}
let holdable = self.holding_is_possible();
for id in self.unattended() {
match self.begin(&id) {
Ok(run) => {
self.begun(&id, holdable);
let began = self.now();
running.push(Attending::begun(id, run, began));
}
Err(refusal) => self.ended(&id, Err(refusal)),
}
}
}
fn attend_one(&mut self, attending: &mut Attending<This::Id, This::Run, This::Moment>) -> bool {
let over = self.run_is_over(&attending.run);
for report in self.read_run(&mut attending.run) {
self.heard(&attending.id, report);
}
if !over {
match self.wanted(&attending.id) {
Wanted::Running => self.let_go(attending),
Wanted::Held => self.hold_still(attending),
Wanted::Unwanted => self.abandon_run(&mut attending.run),
}
}
let elapsed = self.running_for(attending);
self.ran_for(&attending.id, elapsed);
!over
}
fn hold_still(&self, attending: &mut Attending<This::Id, This::Run, This::Moment>) {
if attending.held_since.is_none() {
self.hold_run(&mut attending.run, true);
attending.held_since = Some(self.now());
}
}
fn let_go(&self, attending: &mut Attending<This::Id, This::Run, This::Moment>) {
if let Some(held_since) = attending.held_since.take() {
attending.held_for = attending.held_for.saturating_add(self.since(&held_since));
self.hold_run(&mut attending.run, false);
}
}
fn running_for(&self, attending: &Attending<This::Id, This::Run, This::Moment>) -> Duration {
let holding = attending.held_since.as_ref().map_or(Duration::ZERO, |at| self.since(at));
self.since(&attending.began).saturating_sub(attending.held_for).saturating_sub(holding)
}
}