use std::path::{Path, PathBuf};
use std::time::Duration;
use crate::login::{LoginView, runs::Runs};
use super::dispatch::Deps;
use super::reply::Reply;
const HOLD_WAITS: u32 = 1875;
const HOLD_TICK: Duration = Duration::from_millis(16);
pub(super) fn start(
deps: &Deps,
ts: &str,
workspace: &Path,
provider: &str,
) -> Result<Reply, String> {
deps.caller
.logins
.start(&deps.world, workspace, provider, &deps.state_root, ts)
.map(Reply::Login)
}
pub(super) fn standing(deps: &Deps, workspace: &Path, provider: &str) -> Reply {
Reply::Login(deps.caller.logins.standing(workspace, provider))
}
pub(crate) enum Frame {
Ready(LoginView),
Waiting,
Over,
}
pub(crate) struct Lane {
runs: Runs,
workspace: PathBuf,
provider: String,
sent: usize,
opened: bool,
done: bool,
waits: u32,
quiet: u32,
tick: Duration,
}
impl Lane {
pub(crate) fn new(runs: Runs, workspace: PathBuf, provider: String) -> Self {
Self::holding(runs, workspace, provider, HOLD_WAITS, HOLD_TICK)
}
pub(crate) fn holding(
runs: Runs,
workspace: PathBuf,
provider: String,
waits: u32,
tick: Duration,
) -> Self {
Self {
runs,
workspace,
provider,
sent: 0,
opened: false,
done: false,
waits,
quiet: 0,
tick,
}
}
pub(crate) fn poll(&mut self) -> Frame {
if self.done {
return Frame::Over;
}
let first = !std::mem::replace(&mut self.opened, true);
let Some(view) = self.runs.frame(&self.workspace, &self.provider, self.sent) else {
return if first {
Frame::Ready(LoginView::default())
} else {
Frame::Over
};
};
self.sent += view.lines.len();
if view.outcome.is_some() {
self.done = true;
return Frame::Ready(view);
}
if first || !view.lines.is_empty() {
return Frame::Ready(view);
}
Frame::Waiting
}
}
impl Iterator for Lane {
type Item = Reply;
fn next(&mut self) -> Option<Reply> {
loop {
match self.poll() {
Frame::Ready(view) => {
self.quiet = 0;
return Some(Reply::Login(view));
}
Frame::Over => return None,
Frame::Waiting => {
self.quiet += 1;
if self.quiet >= self.waits {
return None;
}
std::thread::sleep(self.tick);
}
}
}
}
}
#[cfg(test)]
mod tests;