use crate::ui_state::Clock;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::{Duration, Instant};
pub struct WoundGrace {
clock: Arc<dyn Clock>,
watching: Option<(PathBuf, String, Instant)>,
}
impl WoundGrace {
pub fn new(clock: Arc<dyn Clock>) -> Self {
Self {
clock,
watching: None,
}
}
pub fn paints(
&mut self,
workspace: &Path,
agent_id: &str,
wounded: bool,
grace: Duration,
) -> bool {
if !wounded {
self.watching = None;
return false;
}
let now = self.clock.now();
let since = match &self.watching {
Some((ws, agent, since)) if ws == workspace && agent == agent_id => *since,
_ => {
self.watching = Some((workspace.to_path_buf(), agent_id.to_string(), now));
now
}
};
now.saturating_duration_since(since) >= grace
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_support::FakeClock;
fn window() -> Duration {
super::super::Cadence::default().wound_grace()
}
const WS: &str = "/w";
const AGENT: &str = "c-1";
fn ws() -> PathBuf {
PathBuf::from(WS)
}
#[test]
fn a_wound_that_clears_inside_the_window_never_paints() {
let clock = FakeClock::new();
let mut grace = WoundGrace::new(clock.arc());
assert!(
!grace.paints(&ws(), AGENT, true, window()),
"not on first sight"
);
clock.advance(Duration::from_millis(900));
assert!(
!grace.paints(&ws(), AGENT, true, window()),
"still inside the window"
);
assert!(!grace.paints(&ws(), AGENT, false, window()));
clock.advance(window());
assert!(
!grace.paints(&ws(), AGENT, true, window()),
"window re-opened"
);
}
#[test]
fn a_wound_that_outlives_the_window_paints_and_stays() {
let clock = FakeClock::new();
let mut grace = WoundGrace::new(clock.arc());
assert!(!grace.paints(&ws(), AGENT, true, window()));
clock.advance(window());
assert!(
grace.paints(&ws(), AGENT, true, window()),
"the window elapsed"
);
assert!(
grace.paints(&ws(), AGENT, true, window()),
"and it stays painted"
);
}
#[test]
fn a_new_subject_opens_its_own_window() {
let clock = FakeClock::new();
let mut grace = WoundGrace::new(clock.arc());
assert!(!grace.paints(&ws(), AGENT, true, window()));
clock.advance(window());
assert!(
!grace.paints(&ws(), "c-2", true, window()),
"another agent, own window"
);
assert!(
!grace.paints(&PathBuf::from("/other"), "c-2", true, window()),
"another workspace, own window"
);
clock.advance(window());
assert!(grace.paints(&PathBuf::from("/other"), "c-2", true, window()));
}
#[test]
fn a_longer_grace_holds_the_banner_longer() {
let clock = FakeClock::new();
let mut grace = WoundGrace::new(clock.arc());
let long = Duration::from_secs(30);
assert!(!grace.paints(&ws(), AGENT, true, long));
clock.advance(window());
assert!(
!grace.paints(&ws(), AGENT, true, long),
"the default window is not this gate's"
);
clock.advance(long);
assert!(grace.paints(&ws(), AGENT, true, long));
}
}