use crate::state_machine::rules::StateMachine;
use std::collections::VecDeque;
#[derive(Debug, Clone)]
pub struct HistoryManager {
undo_stack: VecDeque<StateMachine>,
redo_stack: VecDeque<StateMachine>,
max_history: usize,
current: Option<StateMachine>,
}
impl HistoryManager {
pub fn new() -> Self {
Self::with_max_history(50)
}
pub fn with_max_history(max_history: usize) -> Self {
Self {
undo_stack: VecDeque::new(),
redo_stack: VecDeque::new(),
max_history,
current: None,
}
}
pub fn push_state(&mut self, state: StateMachine) {
if let Some(current) = self.current.take() {
if self.undo_stack.len() >= self.max_history {
self.undo_stack.pop_front();
}
self.undo_stack.push_back(current);
}
self.redo_stack.clear();
self.current = Some(state);
}
pub fn undo(&mut self) -> Option<StateMachine> {
if self.undo_stack.is_empty() {
return None;
}
if let Some(current) = self.current.take() {
if self.redo_stack.len() >= self.max_history {
self.redo_stack.pop_front();
}
self.redo_stack.push_back(current);
}
let restored = self.undo_stack.pop_back()?;
self.current = Some(restored.clone());
Some(restored)
}
pub fn redo(&mut self) -> Option<StateMachine> {
if self.redo_stack.is_empty() {
return None;
}
if let Some(current) = self.current.take() {
if self.undo_stack.len() >= self.max_history {
self.undo_stack.pop_front();
}
self.undo_stack.push_back(current);
}
let restored = self.redo_stack.pop_back()?;
self.current = Some(restored.clone());
Some(restored)
}
pub fn can_undo(&self) -> bool {
!self.undo_stack.is_empty()
}
pub fn can_redo(&self) -> bool {
!self.redo_stack.is_empty()
}
pub fn current(&self) -> Option<&StateMachine> {
self.current.as_ref()
}
pub fn current_mut(&mut self) -> Option<&mut StateMachine> {
self.current.as_mut()
}
pub fn set_current(&mut self, state: StateMachine) {
self.current = Some(state);
}
pub fn clear(&mut self) {
self.undo_stack.clear();
self.redo_stack.clear();
self.current = None;
}
pub fn undo_count(&self) -> usize {
self.undo_stack.len()
}
pub fn redo_count(&self) -> usize {
self.redo_stack.len()
}
pub fn max_history(&self) -> usize {
self.max_history
}
pub fn set_max_history(&mut self, max_history: usize) {
self.max_history = max_history;
while self.undo_stack.len() > max_history {
self.undo_stack.pop_front();
}
while self.redo_stack.len() > max_history {
self.redo_stack.pop_front();
}
}
}
impl Default for HistoryManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::state_machine::rules::{StateMachine, StateTransition};
fn create_test_machine(name: &str) -> StateMachine {
StateMachine::new(
format!("resource_{}", name),
vec!["state1".to_string(), "state2".to_string()],
"state1",
)
.add_transition(StateTransition::new("state1", "state2"))
}
#[test]
fn test_history_manager_creation() {
let manager = HistoryManager::new();
assert!(!manager.can_undo());
assert!(!manager.can_redo());
}
#[test]
fn test_push_and_undo() {
let mut manager = HistoryManager::new();
let state1 = create_test_machine("1");
let state2 = create_test_machine("2");
manager.push_state(state1.clone());
manager.push_state(state2.clone());
assert!(manager.can_undo());
let restored = manager.undo().unwrap();
assert_eq!(restored.resource_type, state1.resource_type);
}
#[test]
fn test_undo_redo() {
let mut manager = HistoryManager::new();
let state1 = create_test_machine("1");
let state2 = create_test_machine("2");
manager.push_state(state1.clone());
manager.push_state(state2.clone());
let restored = manager.undo().unwrap();
assert_eq!(restored.resource_type, state1.resource_type);
let restored = manager.redo().unwrap();
assert_eq!(restored.resource_type, state2.resource_type);
}
#[test]
fn test_max_history() {
let mut manager = HistoryManager::with_max_history(3);
for i in 0..5 {
manager.push_state(create_test_machine(&i.to_string()));
}
assert_eq!(manager.undo_count(), 3);
}
#[test]
fn test_clear_redo_on_new_push() {
let mut manager = HistoryManager::new();
let state1 = create_test_machine("1");
let state2 = create_test_machine("2");
let state3 = create_test_machine("3");
manager.push_state(state1);
manager.push_state(state2);
manager.undo(); assert!(manager.can_redo());
manager.push_state(state3); assert!(!manager.can_redo());
}
}