use std::path::PathBuf; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::NominaError; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UndoLog { pub entries: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UndoBatch { pub id: Uuid, pub timestamp: DateTime, pub description: String, pub operations: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UndoEntry { pub original_path: PathBuf, pub renamed_path: PathBuf, } const MAX_UNDO_BATCHES: usize = 50; impl UndoLog { pub fn new() -> Self { Self { entries: Vec::new(), } } pub fn load(path: &std::path::Path) -> crate::Result { if !path.exists() { return Ok(Self::new()); } let data = std::fs::read_to_string(path).map_err(|e| NominaError::Filesystem { path: path.to_path_buf(), source: e, })?; serde_json::from_str(&data).map_err(|e| NominaError::PresetError { reason: e.to_string(), }) } pub fn save(&self, path: &std::path::Path) -> crate::Result<()> { if let Some(parent) = path.parent() { std::fs::create_dir_all(parent).map_err(|e| NominaError::Filesystem { path: parent.to_path_buf(), source: e, })?; } let json = serde_json::to_string_pretty(self).map_err(|e| NominaError::PresetError { reason: e.to_string(), })?; std::fs::write(path, json).map_err(|e| NominaError::Filesystem { path: path.to_path_buf(), source: e, }) } pub fn add_batch(&mut self, batch: UndoBatch) { self.entries.push(batch); while self.entries.len() > MAX_UNDO_BATCHES { self.entries.remove(0); } } pub fn undo_last(&mut self) -> Option { self.entries.pop() } pub fn undo_by_id(&mut self, id: Uuid) -> Option { if let Some(pos) = self.entries.iter().position(|b| b.id == id) { Some(self.entries.remove(pos)) } else { None } } pub fn clear(&mut self) { self.entries.clear(); } }