use std::sync::atomic::{AtomicU64, Ordering};
use crate::error::{Error, Result};
use crate::types::SESSION_PAUSE_DURATION_MS;
pub(crate) struct SessionGuard {
pause_until_ms: AtomicU64,
}
impl SessionGuard {
pub fn new() -> Self {
Self {
pause_until_ms: AtomicU64::new(0),
}
}
pub fn pause(&self) {
let until = now_ms() + SESSION_PAUSE_DURATION_MS;
self.pause_until_ms.store(until, Ordering::Relaxed);
tracing::info!(until_ms = until, "session paused");
}
pub fn is_paused(&self) -> bool {
let until = self.pause_until_ms.load(Ordering::Relaxed);
until > 0 && now_ms() < until
}
pub fn remaining_ms(&self) -> u64 {
let until = self.pause_until_ms.load(Ordering::Relaxed);
if until == 0 {
return 0;
}
until.saturating_sub(now_ms())
}
#[allow(dead_code)] pub fn assert_active(&self) -> Result<()> {
if self.is_paused() {
Err(Error::SessionExpired)
} else {
Ok(())
}
}
}
impl Default for SessionGuard {
fn default() -> Self {
Self::new()
}
}
use crate::util::now_ms;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn initial_state_not_paused() {
let guard = SessionGuard::new();
assert!(!guard.is_paused());
assert_eq!(guard.remaining_ms(), 0);
assert!(guard.assert_active().is_ok());
}
#[test]
fn pause_and_check() {
let guard = SessionGuard::new();
guard.pause();
assert!(guard.is_paused());
assert!(guard.remaining_ms() > 0);
}
#[test]
fn assert_active_when_paused() {
let guard = SessionGuard::new();
guard.pause();
assert!(guard.assert_active().is_err());
}
}