use std::sync::Mutex;
use std::sync::atomic::{AtomicI64, Ordering};
pub struct Activity {
last_activity_ms: AtomicI64,
last_tool: Mutex<Option<String>>,
last_tool_ms: AtomicI64,
}
impl Default for Activity {
fn default() -> Self {
Self::new()
}
}
impl Activity {
pub fn new() -> Self {
Self {
last_activity_ms: AtomicI64::new(now_ms()),
last_tool: Mutex::new(None),
last_tool_ms: AtomicI64::new(0),
}
}
pub fn start(&self) {
self.bump();
}
pub fn end(&self) {
self.bump();
}
pub fn bump(&self) {
self.last_activity_ms.store(now_ms(), Ordering::Relaxed);
}
pub fn last_activity_ms(&self) -> i64 {
self.last_activity_ms.load(Ordering::Relaxed)
}
pub fn record_tool(&self, name: &str) {
let now = now_ms();
self.last_activity_ms.store(now, Ordering::Relaxed);
self.last_tool_ms.store(now, Ordering::Relaxed);
if let Ok(mut slot) = self.last_tool.lock() {
*slot = Some(name.to_string());
}
}
pub fn last_tool_name(&self) -> Option<String> {
self.last_tool.lock().ok().and_then(|slot| slot.clone())
}
pub fn last_tool_ms(&self) -> i64 {
self.last_tool_ms.load(Ordering::Relaxed)
}
}
fn now_ms() -> i64 {
chrono::Utc::now().timestamp_millis()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn record_tool_sets_name_and_timestamp() {
let a = Activity::new();
a.record_tool("send_message");
assert_eq!(a.last_tool_name().as_deref(), Some("send_message"));
assert!(a.last_tool_ms() > 0);
}
#[test]
fn bump_advances_last_activity() {
let a = Activity::new();
let before = a.last_activity_ms();
std::thread::sleep(std::time::Duration::from_millis(5));
a.bump();
assert!(a.last_activity_ms() >= before);
}
}