use std::time::Instant;
#[derive(Debug, Clone)]
pub struct TranslationDelta {
changes: Vec<RecordChange>,
undo_stack: Vec<usize>,
redo_stack: Vec<usize>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RecordChange {
pub record_id: RecordId,
pub subrecord_type: String,
pub old_value: String,
pub new_value: String,
pub applied_at: Instant,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RecordId {
pub form_id: u32,
pub editor_id: Option<String>,
}
impl RecordId {
pub fn new(form_id: u32, editor_id: Option<String>) -> Self {
Self { form_id, editor_id }
}
pub fn from_form_id(form_id: u32) -> Self {
Self {
form_id,
editor_id: None,
}
}
}
impl TranslationDelta {
pub fn new() -> Self {
Self {
changes: Vec::new(),
undo_stack: Vec::new(),
redo_stack: Vec::new(),
}
}
pub fn add_change(&mut self, change: RecordChange) {
let index = self.changes.len();
self.changes.push(change);
self.undo_stack.push(index);
self.redo_stack.clear(); }
pub fn undo(&mut self) -> Result<&RecordChange, String> {
let index = self
.undo_stack
.pop()
.ok_or_else(|| "没有可撤销的操作".to_string())?;
self.redo_stack.push(index);
Ok(&self.changes[index])
}
pub fn redo(&mut self) -> Result<&RecordChange, String> {
let index = self
.redo_stack
.pop()
.ok_or_else(|| "没有可重做的操作".to_string())?;
self.undo_stack.push(index);
Ok(&self.changes[index])
}
pub fn len(&self) -> usize {
self.undo_stack.len()
}
pub fn is_empty(&self) -> bool {
self.undo_stack.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &RecordChange> {
self.undo_stack.iter().map(|&idx| &self.changes[idx])
}
pub fn all_changes(&self) -> &[RecordChange] {
&self.changes
}
pub fn can_undo(&self) -> bool {
!self.undo_stack.is_empty()
}
pub fn can_redo(&self) -> bool {
!self.redo_stack.is_empty()
}
pub fn clear(&mut self) {
self.changes.clear();
self.undo_stack.clear();
self.redo_stack.clear();
}
pub fn get_changes_for_record(&self, record_id: &RecordId) -> Vec<&RecordChange> {
self.iter()
.filter(|change| &change.record_id == record_id)
.collect()
}
pub fn summary(&self) -> String {
format!(
"变更总数: {}, 有效变更: {}, 可撤销: {}, 可重做: {}",
self.changes.len(),
self.undo_stack.len(),
self.can_undo(),
self.can_redo()
)
}
}
impl Default for TranslationDelta {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for RecordChange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"[{:08X}] {}: \"{}\" -> \"{}\"",
self.record_id.form_id,
self.subrecord_type,
if self.old_value.len() > 30 {
format!("{}...", &self.old_value[..30])
} else {
self.old_value.clone()
},
if self.new_value.len() > 30 {
format!("{}...", &self.new_value[..30])
} else {
self.new_value.clone()
}
)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_change(form_id: u32, old: &str, new: &str) -> RecordChange {
RecordChange {
record_id: RecordId::from_form_id(form_id),
subrecord_type: "FULL".to_string(),
old_value: old.to_string(),
new_value: new.to_string(),
applied_at: Instant::now(),
}
}
#[test]
fn test_delta_basic() {
let mut delta = TranslationDelta::new();
assert_eq!(delta.len(), 0);
assert!(delta.is_empty());
delta.add_change(create_test_change(1, "old", "new"));
assert_eq!(delta.len(), 1);
assert!(!delta.is_empty());
}
#[test]
fn test_undo_redo() {
let mut delta = TranslationDelta::new();
delta.add_change(create_test_change(1, "a", "b"));
delta.add_change(create_test_change(2, "c", "d"));
delta.add_change(create_test_change(3, "e", "f"));
assert_eq!(delta.len(), 3);
let undone = delta.undo().unwrap();
assert_eq!(undone.record_id.form_id, 3);
assert_eq!(delta.len(), 2);
delta.undo().unwrap();
assert_eq!(delta.len(), 1);
let redone = delta.redo().unwrap();
assert_eq!(redone.record_id.form_id, 2);
assert_eq!(delta.len(), 2);
}
#[test]
fn test_new_change_clears_redo() {
let mut delta = TranslationDelta::new();
delta.add_change(create_test_change(1, "a", "b"));
delta.add_change(create_test_change(2, "c", "d"));
delta.undo().unwrap();
assert!(delta.can_redo());
delta.add_change(create_test_change(3, "e", "f"));
assert!(!delta.can_redo());
}
#[test]
fn test_undo_when_empty() {
let mut delta = TranslationDelta::new();
let result = delta.undo();
assert!(result.is_err());
}
#[test]
fn test_redo_when_empty() {
let mut delta = TranslationDelta::new();
let result = delta.redo();
assert!(result.is_err());
}
#[test]
fn test_get_changes_for_record() {
let mut delta = TranslationDelta::new();
let record_id = RecordId::from_form_id(100);
delta.add_change(RecordChange {
record_id: record_id.clone(),
subrecord_type: "FULL".to_string(),
old_value: "old1".to_string(),
new_value: "new1".to_string(),
applied_at: Instant::now(),
});
delta.add_change(create_test_change(200, "x", "y"));
delta.add_change(RecordChange {
record_id: record_id.clone(),
subrecord_type: "DESC".to_string(),
old_value: "old2".to_string(),
new_value: "new2".to_string(),
applied_at: Instant::now(),
});
let changes = delta.get_changes_for_record(&record_id);
assert_eq!(changes.len(), 2);
}
#[test]
fn test_clear() {
let mut delta = TranslationDelta::new();
delta.add_change(create_test_change(1, "a", "b"));
delta.add_change(create_test_change(2, "c", "d"));
delta.clear();
assert_eq!(delta.len(), 0);
assert!(delta.is_empty());
assert!(!delta.can_undo());
assert!(!delta.can_redo());
}
#[test]
fn test_summary() {
let mut delta = TranslationDelta::new();
delta.add_change(create_test_change(1, "a", "b"));
delta.add_change(create_test_change(2, "c", "d"));
let summary = delta.summary();
assert!(summary.contains("变更总数: 2"));
assert!(summary.contains("有效变更: 2"));
}
}