use crate::core::Msg;
use std::collections::VecDeque;
use std::time::{Instant, Duration};
pub struct ActionLogger {
history: VecDeque<ActionRecord>,
max_history: usize,
start_time: Instant,
enabled: bool,
}
#[derive(Debug, Clone)]
pub struct ActionRecord {
pub action: String,
pub elapsed: Duration,
pub produced_effect: bool,
}
impl ActionLogger {
pub fn new(max_history: usize) -> Self {
Self {
history: VecDeque::with_capacity(max_history),
max_history,
start_time: Instant::now(),
enabled: true,
}
}
pub fn log_action(&mut self, msg: &Msg, produced_effect: bool) {
if !self.enabled {
return;
}
let record = ActionRecord {
action: format!("{:?}", msg),
elapsed: self.start_time.elapsed(),
produced_effect,
};
if produced_effect {
tracing::debug!("ACTION [+effect]: {}", record.action);
} else {
tracing::trace!("ACTION: {}", record.action);
}
if self.history.len() >= self.max_history {
self.history.pop_front();
}
self.history.push_back(record);
}
pub fn recent_actions(&self, count: usize) -> Vec<&ActionRecord> {
self.history.iter().rev().take(count).collect()
}
pub fn all_actions(&self) -> Vec<&ActionRecord> {
self.history.iter().collect()
}
pub fn action_count(&self) -> usize {
self.history.len()
}
pub fn action_stats(&self) -> std::collections::HashMap<String, usize> {
let mut stats = std::collections::HashMap::new();
for record in &self.history {
let action_type = record.action
.split(|c| c == ' ' || c == '{' || c == '(')
.next()
.unwrap_or("Unknown")
.to_string();
*stats.entry(action_type).or_insert(0) += 1;
}
stats
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
pub fn clear(&mut self) {
self.history.clear();
}
}
impl Default for ActionLogger {
fn default() -> Self {
Self::new(1000) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_action_logging() {
let mut logger = ActionLogger::new(10);
logger.log_action(&Msg::Tick, false);
logger.log_action(&Msg::ToggleHelp, false);
assert_eq!(logger.action_count(), 2);
let recent = logger.recent_actions(5);
assert_eq!(recent.len(), 2);
}
#[test]
fn test_ring_buffer() {
let mut logger = ActionLogger::new(3);
for _ in 0..5 {
logger.log_action(&Msg::Tick, false);
}
assert_eq!(logger.action_count(), 3);
}
}