use std::time::{Duration, Instant};
pub const WINDOW: Duration = Duration::from_secs(3);
pub const NOTICE: &str =
"turn still running in the daemon \u{2014} press Esc to abort it, or Ctrl+C again within 3 s to detach (session keeps running)";
pub const NOTICE_LINE: &str =
"turn still running in the daemon \u{2014} Ctrl+C again within 3 s to detach (session keeps running)";
#[derive(Debug, Clone)]
pub struct QuitGuard {
first_press: Option<Instant>,
}
impl QuitGuard {
pub fn new() -> Self {
Self { first_press: None }
}
pub fn press(&mut self, now: Instant) -> bool {
if let Some(first) = self.first_press {
if now.duration_since(first) <= WINDOW {
self.first_press = None;
return true;
}
}
self.first_press = Some(now);
false
}
pub fn reset(&mut self) {
self.first_press = None;
}
}
impl Default for QuitGuard {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn fake_now(offset: Duration) -> Instant {
use std::sync::OnceLock;
static ANCHOR: OnceLock<Instant> = OnceLock::new();
*ANCHOR.get_or_init(Instant::now) + offset
}
#[test]
fn first_press_returns_false() {
let mut g = QuitGuard::new();
assert!(!g.press(fake_now(Duration::from_secs(0))));
}
#[test]
fn second_press_within_window_returns_true() {
let mut g = QuitGuard::new();
assert!(!g.press(fake_now(Duration::from_secs(0))));
assert!(g.press(fake_now(Duration::from_secs(2))));
}
#[test]
fn second_press_after_window_returns_false() {
let mut g = QuitGuard::new();
assert!(!g.press(fake_now(Duration::from_secs(0))));
assert!(!g.press(fake_now(Duration::from_secs(4))));
}
#[test]
fn third_press_within_new_window_returns_true() {
let mut g = QuitGuard::new();
assert!(!g.press(fake_now(Duration::from_secs(0))));
assert!(!g.press(fake_now(Duration::from_secs(4))));
assert!(g.press(fake_now(Duration::from_secs(5))));
}
#[test]
fn reset_clears_state() {
let mut g = QuitGuard::new();
assert!(!g.press(fake_now(Duration::from_secs(0))));
g.reset();
assert!(!g.press(fake_now(Duration::from_secs(1))));
}
#[test]
fn exact_boundary_is_within_window() {
let mut g = QuitGuard::new();
assert!(!g.press(fake_now(Duration::from_secs(0))));
assert!(g.press(fake_now(Duration::from_secs(3))));
}
#[test]
fn just_past_boundary_is_outside_window() {
let mut g = QuitGuard::new();
assert!(!g.press(fake_now(Duration::from_secs(0))));
assert!(!g.press(fake_now(Duration::from_millis(3001))));
}
}