1use std::any::Any;
12use std::collections::HashMap;
13use std::sync::Arc;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub struct CommandId(u64);
18
19impl CommandId {
20 pub fn as_u64(self) -> u64 {
21 self.0
22 }
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
27pub struct GroupId(u64);
28
29impl GroupId {
30 pub fn as_u64(self) -> u64 {
31 self.0
32 }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37pub struct CheckpointId(u64);
38
39impl CheckpointId {
40 pub fn as_u64(self) -> u64 {
41 self.0
42 }
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
47pub enum CommandResult {
48 Success,
50 Failed(String),
52 Cancelled,
54 NeedsConfirmation(String),
56}
57
58impl CommandResult {
59 pub fn is_success(&self) -> bool {
60 matches!(self, Self::Success)
61 }
62
63 pub fn is_failed(&self) -> bool {
64 matches!(self, Self::Failed(_))
65 }
66}
67
68pub trait Command: Send + Sync {
70 fn execute(&mut self) -> CommandResult;
72
73 fn undo(&mut self) -> CommandResult;
75
76 fn redo(&mut self) -> CommandResult {
78 self.execute()
79 }
80
81 fn description(&self) -> &str;
83
84 fn can_merge(&self, _other: &dyn Command) -> bool {
86 false
87 }
88
89 fn merge(&mut self, _other: Box<dyn Command>) -> Option<Box<dyn Command>> {
91 None
92 }
93
94 fn memory_size(&self) -> usize {
96 64
98 }
99
100 fn metadata(&self) -> Option<&dyn Any> {
102 None
103 }
104}
105
106struct HistoryEntry {
108 #[allow(dead_code)]
109 id: CommandId,
110 command: Box<dyn Command>,
111 group_id: Option<GroupId>,
112 timestamp: u64,
113 #[allow(dead_code)]
114 executed: bool,
115}
116
117#[derive(Debug, Clone)]
119pub struct HistoryConfig {
120 pub max_commands: usize,
122 pub max_memory: usize,
124 pub enable_merging: bool,
126 pub auto_group_interval_ms: u64,
128}
129
130impl Default for HistoryConfig {
131 fn default() -> Self {
132 Self {
133 max_commands: 1000,
134 max_memory: 10 * 1024 * 1024, enable_merging: true,
136 auto_group_interval_ms: 500,
137 }
138 }
139}
140
141#[derive(Debug, Clone)]
143pub struct Checkpoint {
144 pub id: CheckpointId,
145 pub name: String,
146 pub position: usize,
147 pub timestamp: u64,
148}
149
150#[derive(Debug, Clone, PartialEq, Eq)]
152pub enum HistoryEvent {
153 Executed(CommandId),
155 Undone(CommandId),
157 Redone(CommandId),
159 Cleared,
161 CheckpointCreated(CheckpointId),
163 CheckpointRestored(CheckpointId),
165 Trimmed(usize),
167}
168
169pub type HistoryCallback = Arc<dyn Fn(HistoryEvent) + Send + Sync>;
171
172pub struct CommandHistory {
174 config: HistoryConfig,
175 next_command_id: u64,
176 next_group_id: u64,
177 next_checkpoint_id: u64,
178
179 undo_stack: Vec<HistoryEntry>,
181 redo_stack: Vec<HistoryEntry>,
183
184 current_group: Option<GroupId>,
186 checkpoints: HashMap<CheckpointId, Checkpoint>,
188
189 timestamp: u64,
191 last_command_time: u64,
193
194 listeners: Vec<HistoryCallback>,
196
197 current_memory: usize,
199
200 recording: bool,
202}
203
204impl std::fmt::Debug for CommandHistory {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 f.debug_struct("CommandHistory")
207 .field("config", &self.config)
208 .field("next_command_id", &self.next_command_id)
209 .field("next_group_id", &self.next_group_id)
210 .field("next_checkpoint_id", &self.next_checkpoint_id)
211 .field("undo_stack_len", &self.undo_stack.len())
212 .field("redo_stack_len", &self.redo_stack.len())
213 .field("current_group", &self.current_group)
214 .field("checkpoints_count", &self.checkpoints.len())
215 .field("timestamp", &self.timestamp)
216 .field("last_command_time", &self.last_command_time)
217 .field("listeners_count", &self.listeners.len())
218 .field("current_memory", &self.current_memory)
219 .field("recording", &self.recording)
220 .finish()
221 }
222}
223
224impl Default for CommandHistory {
225 fn default() -> Self {
226 Self::new(HistoryConfig::default())
227 }
228}
229
230impl CommandHistory {
231 pub fn new(config: HistoryConfig) -> Self {
232 Self {
233 config,
234 next_command_id: 1,
235 next_group_id: 1,
236 next_checkpoint_id: 1,
237 undo_stack: Vec::new(),
238 redo_stack: Vec::new(),
239 current_group: None,
240 checkpoints: HashMap::new(),
241 timestamp: 0,
242 last_command_time: 0,
243 listeners: Vec::new(),
244 current_memory: 0,
245 recording: true,
246 }
247 }
248
249 pub fn execute(&mut self, mut command: Box<dyn Command>) -> CommandResult {
251 if !self.recording {
252 return command.execute();
253 }
254
255 let result = command.execute();
256 if !result.is_success() {
257 return result;
258 }
259
260 self.redo_stack.clear();
262
263 let id = CommandId(self.next_command_id);
264 self.next_command_id += 1;
265
266 let group_id = if self.current_group.is_some() {
268 self.current_group
269 } else if self.config.auto_group_interval_ms > 0
270 && self.timestamp.saturating_sub(self.last_command_time)
271 < self.config.auto_group_interval_ms
272 && !self.undo_stack.is_empty()
273 {
274 self.undo_stack.last().and_then(|e| e.group_id)
276 } else {
277 None
278 };
279
280 if self.config.enable_merging && !self.undo_stack.is_empty() {
282 let can_merge = self
283 .undo_stack
284 .last()
285 .is_some_and(|last| last.command.can_merge(command.as_ref()));
286
287 if can_merge {
288 if let Some(last_entry) = self.undo_stack.last_mut() {
289 if let Some(merged) = last_entry.command.merge(command) {
290 self.current_memory -= last_entry.command.memory_size();
292 self.current_memory += merged.memory_size();
293 last_entry.command = merged;
294 self.emit(&HistoryEvent::Executed(id));
295 return result;
296 }
297 }
298 return result;
301 }
302 }
303
304 let memory = command.memory_size();
305 self.current_memory += memory;
306
307 let entry = HistoryEntry {
308 id,
309 command,
310 group_id,
311 timestamp: self.timestamp,
312 executed: true,
313 };
314
315 self.undo_stack.push(entry);
316 self.last_command_time = self.timestamp;
317
318 self.trim_if_needed();
320
321 self.emit(&HistoryEvent::Executed(id));
322 result
323 }
324
325 pub fn undo(&mut self) -> Option<CommandResult> {
327 let entry = self.undo_stack.pop()?;
328 let id = entry.id;
329 let group_id = entry.group_id;
330
331 let mut command = entry.command;
332 let result = command.undo();
333
334 if result.is_success() {
335 self.current_memory -= command.memory_size();
336
337 let redo_entry = HistoryEntry {
338 id,
339 command,
340 group_id,
341 timestamp: entry.timestamp,
342 executed: false,
343 };
344 self.current_memory += redo_entry.command.memory_size();
345 self.redo_stack.push(redo_entry);
346
347 self.emit(&HistoryEvent::Undone(id));
348
349 if let Some(gid) = group_id {
351 while let Some(last) = self.undo_stack.last() {
352 if last.group_id == Some(gid) {
353 self.undo();
354 } else {
355 break;
356 }
357 }
358 }
359 }
360
361 Some(result)
362 }
363
364 pub fn redo(&mut self) -> Option<CommandResult> {
366 let entry = self.redo_stack.pop()?;
367 let id = entry.id;
368 let group_id = entry.group_id;
369
370 let mut command = entry.command;
371 let result = command.redo();
372
373 if result.is_success() {
374 self.current_memory -= command.memory_size();
375
376 let undo_entry = HistoryEntry {
377 id,
378 command,
379 group_id,
380 timestamp: entry.timestamp,
381 executed: true,
382 };
383 self.current_memory += undo_entry.command.memory_size();
384 self.undo_stack.push(undo_entry);
385
386 self.emit(&HistoryEvent::Redone(id));
387
388 if let Some(gid) = group_id {
390 while let Some(last) = self.redo_stack.last() {
391 if last.group_id == Some(gid) {
392 self.redo();
393 } else {
394 break;
395 }
396 }
397 }
398 }
399
400 Some(result)
401 }
402
403 pub fn can_undo(&self) -> bool {
405 !self.undo_stack.is_empty()
406 }
407
408 pub fn can_redo(&self) -> bool {
410 !self.redo_stack.is_empty()
411 }
412
413 pub fn undo_count(&self) -> usize {
415 self.undo_stack.len()
416 }
417
418 pub fn redo_count(&self) -> usize {
420 self.redo_stack.len()
421 }
422
423 pub fn undo_description(&self) -> Option<&str> {
425 self.undo_stack.last().map(|e| e.command.description())
426 }
427
428 pub fn redo_description(&self) -> Option<&str> {
430 self.redo_stack.last().map(|e| e.command.description())
431 }
432
433 pub fn begin_group(&mut self) -> GroupId {
435 let id = GroupId(self.next_group_id);
436 self.next_group_id += 1;
437 self.current_group = Some(id);
438 id
439 }
440
441 pub fn end_group(&mut self) {
443 self.current_group = None;
444 }
445
446 pub fn execute_group<I>(&mut self, commands: I) -> Vec<CommandResult>
448 where
449 I: IntoIterator<Item = Box<dyn Command>>,
450 {
451 let _group = self.begin_group();
452 let results: Vec<_> = commands.into_iter().map(|cmd| self.execute(cmd)).collect();
453 self.end_group();
454 results
455 }
456
457 pub fn create_checkpoint(&mut self, name: impl Into<String>) -> CheckpointId {
459 let id = CheckpointId(self.next_checkpoint_id);
460 self.next_checkpoint_id += 1;
461
462 let checkpoint = Checkpoint {
463 id,
464 name: name.into(),
465 position: self.undo_stack.len(),
466 timestamp: self.timestamp,
467 };
468
469 self.checkpoints.insert(id, checkpoint);
470 self.emit(&HistoryEvent::CheckpointCreated(id));
471 id
472 }
473
474 pub fn restore_checkpoint(&mut self, id: CheckpointId) -> bool {
476 let checkpoint = match self.checkpoints.get(&id) {
477 Some(c) => c.clone(),
478 None => return false,
479 };
480
481 while self.undo_stack.len() > checkpoint.position {
483 if self.undo().is_none() {
484 break;
485 }
486 }
487
488 self.emit(&HistoryEvent::CheckpointRestored(id));
489 true
490 }
491
492 pub fn get_checkpoint(&self, id: CheckpointId) -> Option<&Checkpoint> {
494 self.checkpoints.get(&id)
495 }
496
497 pub fn checkpoints(&self) -> impl Iterator<Item = &Checkpoint> {
499 self.checkpoints.values()
500 }
501
502 pub fn clear(&mut self) {
504 self.undo_stack.clear();
505 self.redo_stack.clear();
506 self.checkpoints.clear();
507 self.current_memory = 0;
508 self.emit(&HistoryEvent::Cleared);
509 }
510
511 pub fn memory_usage(&self) -> usize {
513 self.current_memory
514 }
515
516 pub fn tick(&mut self, delta_ms: u64) {
518 self.timestamp += delta_ms;
519 }
520
521 pub fn pause(&mut self) {
523 self.recording = false;
524 }
525
526 pub fn resume(&mut self) {
528 self.recording = true;
529 }
530
531 pub fn is_recording(&self) -> bool {
533 self.recording
534 }
535
536 pub fn on_event(&mut self, callback: HistoryCallback) {
538 self.listeners.push(callback);
539 }
540
541 fn emit(&self, event: &HistoryEvent) {
542 for listener in &self.listeners {
543 listener(event.clone());
544 }
545 }
546
547 fn trim_if_needed(&mut self) {
548 let mut trimmed = 0;
549
550 while self.undo_stack.len() > self.config.max_commands {
552 if let Some(entry) = self.undo_stack.first() {
553 self.current_memory -= entry.command.memory_size();
554 }
555 self.undo_stack.remove(0);
556 trimmed += 1;
557 }
558
559 while self.current_memory > self.config.max_memory && !self.undo_stack.is_empty() {
561 if let Some(entry) = self.undo_stack.first() {
562 self.current_memory -= entry.command.memory_size();
563 }
564 self.undo_stack.remove(0);
565 trimmed += 1;
566 }
567
568 if trimmed > 0 {
569 for checkpoint in self.checkpoints.values_mut() {
571 checkpoint.position = checkpoint.position.saturating_sub(trimmed);
572 }
573 self.emit(&HistoryEvent::Trimmed(trimmed));
574 }
575 }
576}
577
578pub struct CompositeCommand {
580 description: String,
581 commands: Vec<Box<dyn Command>>,
582 executed: Vec<bool>,
583}
584
585impl std::fmt::Debug for CompositeCommand {
586 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
587 f.debug_struct("CompositeCommand")
588 .field("description", &self.description)
589 .field("commands_count", &self.commands.len())
590 .field("executed", &self.executed)
591 .finish()
592 }
593}
594
595impl CompositeCommand {
596 pub fn new(description: impl Into<String>) -> Self {
597 Self {
598 description: description.into(),
599 commands: Vec::new(),
600 executed: Vec::new(),
601 }
602 }
603
604 pub fn with_command(mut self, command: Box<dyn Command>) -> Self {
605 self.commands.push(command);
606 self
607 }
608
609 pub fn build(self) -> Box<dyn Command> {
610 Box::new(CompositeCommandImpl {
611 description: self.description,
612 commands: self.commands,
613 executed: self.executed,
614 })
615 }
616}
617
618struct CompositeCommandImpl {
619 description: String,
620 commands: Vec<Box<dyn Command>>,
621 executed: Vec<bool>,
622}
623
624impl Command for CompositeCommandImpl {
625 fn execute(&mut self) -> CommandResult {
626 self.executed.clear();
627 for cmd in &mut self.commands {
628 let result = cmd.execute();
629 if !result.is_success() {
630 for (i, was_executed) in self.executed.iter().enumerate().rev() {
632 if *was_executed {
633 self.commands[i].undo();
634 }
635 }
636 return result;
637 }
638 self.executed.push(true);
639 }
640 CommandResult::Success
641 }
642
643 fn undo(&mut self) -> CommandResult {
644 for (i, cmd) in self.commands.iter_mut().enumerate().rev() {
645 if i < self.executed.len() && self.executed[i] {
646 let result = cmd.undo();
647 if !result.is_success() {
648 return result;
649 }
650 }
651 }
652 self.executed.clear();
653 CommandResult::Success
654 }
655
656 fn description(&self) -> &str {
657 &self.description
658 }
659
660 fn memory_size(&self) -> usize {
661 std::mem::size_of::<Self>() + self.commands.iter().map(|c| c.memory_size()).sum::<usize>()
662 }
663}
664
665pub struct SetValueCommand<T: Clone + Send + Sync + 'static> {
667 description: String,
668 value: Arc<std::sync::RwLock<T>>,
669 old_value: Option<T>,
670 new_value: T,
671}
672
673impl<T: Clone + Send + Sync + 'static + std::fmt::Debug> std::fmt::Debug for SetValueCommand<T> {
674 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
675 f.debug_struct("SetValueCommand")
676 .field("description", &self.description)
677 .field("old_value", &self.old_value)
678 .field("new_value", &self.new_value)
679 .finish_non_exhaustive()
680 }
681}
682
683impl<T: Clone + Send + Sync + 'static> SetValueCommand<T> {
684 pub fn new(
685 description: impl Into<String>,
686 value: Arc<std::sync::RwLock<T>>,
687 new_value: T,
688 ) -> Self {
689 Self {
690 description: description.into(),
691 value,
692 old_value: None,
693 new_value,
694 }
695 }
696}
697
698impl<T: Clone + Send + Sync + 'static> Command for SetValueCommand<T> {
699 fn execute(&mut self) -> CommandResult {
700 let Ok(mut guard) = self.value.write() else {
701 return CommandResult::Failed("Lock poisoned".into());
702 };
703 self.old_value = Some(guard.clone());
704 *guard = self.new_value.clone();
705 CommandResult::Success
706 }
707
708 fn undo(&mut self) -> CommandResult {
709 let Some(old) = self.old_value.clone() else {
710 return CommandResult::Failed("No old value".into());
711 };
712 let Ok(mut guard) = self.value.write() else {
713 return CommandResult::Failed("Lock poisoned".into());
714 };
715 *guard = old;
716 CommandResult::Success
717 }
718
719 fn description(&self) -> &str {
720 &self.description
721 }
722}
723
724#[cfg(test)]
725#[allow(clippy::unwrap_used, clippy::disallowed_methods)]
726mod tests {
727 use super::*;
728 use std::sync::atomic::{AtomicI32, Ordering};
729
730 struct IncrementCommand {
732 counter: Arc<AtomicI32>,
733 amount: i32,
734 }
735
736 impl Command for IncrementCommand {
737 fn execute(&mut self) -> CommandResult {
738 self.counter.fetch_add(self.amount, Ordering::SeqCst);
739 CommandResult::Success
740 }
741
742 fn undo(&mut self) -> CommandResult {
743 self.counter.fetch_sub(self.amount, Ordering::SeqCst);
744 CommandResult::Success
745 }
746
747 fn description(&self) -> &'static str {
748 "Increment counter"
749 }
750 }
751
752 struct FailingCommand {
754 should_fail: bool,
755 }
756
757 impl Command for FailingCommand {
758 fn execute(&mut self) -> CommandResult {
759 if self.should_fail {
760 CommandResult::Failed("Intentional failure".into())
761 } else {
762 CommandResult::Success
763 }
764 }
765
766 fn undo(&mut self) -> CommandResult {
767 CommandResult::Success
768 }
769
770 fn description(&self) -> &'static str {
771 "Failing command"
772 }
773 }
774
775 #[test]
776 fn test_basic_execute() {
777 let mut history = CommandHistory::default();
778 let counter = Arc::new(AtomicI32::new(0));
779
780 let cmd = Box::new(IncrementCommand {
781 counter: counter.clone(),
782 amount: 5,
783 });
784
785 let result = history.execute(cmd);
786 assert!(result.is_success());
787 assert_eq!(counter.load(Ordering::SeqCst), 5);
788 assert_eq!(history.undo_count(), 1);
789 }
790
791 #[test]
792 fn test_undo() {
793 let mut history = CommandHistory::default();
794 let counter = Arc::new(AtomicI32::new(0));
795
796 history.execute(Box::new(IncrementCommand {
797 counter: counter.clone(),
798 amount: 10,
799 }));
800 assert_eq!(counter.load(Ordering::SeqCst), 10);
801
802 let result = history.undo();
803 assert!(result.is_some());
804 assert!(result.unwrap().is_success());
805 assert_eq!(counter.load(Ordering::SeqCst), 0);
806 }
807
808 #[test]
809 fn test_redo() {
810 let mut history = CommandHistory::default();
811 let counter = Arc::new(AtomicI32::new(0));
812
813 history.execute(Box::new(IncrementCommand {
814 counter: counter.clone(),
815 amount: 7,
816 }));
817 history.undo();
818 assert_eq!(counter.load(Ordering::SeqCst), 0);
819
820 let result = history.redo();
821 assert!(result.is_some());
822 assert!(result.unwrap().is_success());
823 assert_eq!(counter.load(Ordering::SeqCst), 7);
824 }
825
826 #[test]
827 fn test_multiple_undo_redo() {
828 let mut history = CommandHistory::default();
829 let counter = Arc::new(AtomicI32::new(0));
830
831 history.execute(Box::new(IncrementCommand {
832 counter: counter.clone(),
833 amount: 1,
834 }));
835 history.execute(Box::new(IncrementCommand {
836 counter: counter.clone(),
837 amount: 2,
838 }));
839 history.execute(Box::new(IncrementCommand {
840 counter: counter.clone(),
841 amount: 3,
842 }));
843
844 assert_eq!(counter.load(Ordering::SeqCst), 6);
845 assert_eq!(history.undo_count(), 3);
846
847 history.undo();
848 assert_eq!(counter.load(Ordering::SeqCst), 3);
849
850 history.undo();
851 assert_eq!(counter.load(Ordering::SeqCst), 1);
852
853 history.redo();
854 assert_eq!(counter.load(Ordering::SeqCst), 3);
855 }
856
857 #[test]
858 fn test_can_undo_redo() {
859 let mut history = CommandHistory::default();
860 let counter = Arc::new(AtomicI32::new(0));
861
862 assert!(!history.can_undo());
863 assert!(!history.can_redo());
864
865 history.execute(Box::new(IncrementCommand { counter, amount: 1 }));
866
867 assert!(history.can_undo());
868 assert!(!history.can_redo());
869
870 history.undo();
871 assert!(!history.can_undo());
872 assert!(history.can_redo());
873 }
874
875 #[test]
876 fn test_redo_cleared_on_new_execute() {
877 let mut history = CommandHistory::default();
878 let counter = Arc::new(AtomicI32::new(0));
879
880 history.execute(Box::new(IncrementCommand {
881 counter: counter.clone(),
882 amount: 1,
883 }));
884 history.execute(Box::new(IncrementCommand {
885 counter: counter.clone(),
886 amount: 2,
887 }));
888
889 history.undo();
890 assert!(history.can_redo());
891
892 history.execute(Box::new(IncrementCommand { counter, amount: 5 }));
894
895 assert!(!history.can_redo());
896 }
897
898 #[test]
899 fn test_failed_command_not_added() {
900 let mut history = CommandHistory::default();
901
902 let result = history.execute(Box::new(FailingCommand { should_fail: true }));
903 assert!(result.is_failed());
904 assert_eq!(history.undo_count(), 0);
905 }
906
907 #[test]
908 fn test_command_descriptions() {
909 let mut history = CommandHistory::default();
910 let counter = Arc::new(AtomicI32::new(0));
911
912 assert!(history.undo_description().is_none());
913
914 history.execute(Box::new(IncrementCommand { counter, amount: 1 }));
915
916 assert_eq!(history.undo_description(), Some("Increment counter"));
917 }
918
919 #[test]
920 fn test_command_groups() {
921 let mut history = CommandHistory::default();
922 let counter = Arc::new(AtomicI32::new(0));
923
924 let _group = history.begin_group();
925 history.execute(Box::new(IncrementCommand {
926 counter: counter.clone(),
927 amount: 1,
928 }));
929 history.execute(Box::new(IncrementCommand {
930 counter: counter.clone(),
931 amount: 2,
932 }));
933 history.execute(Box::new(IncrementCommand {
934 counter: counter.clone(),
935 amount: 3,
936 }));
937 history.end_group();
938
939 assert_eq!(counter.load(Ordering::SeqCst), 6);
940
941 history.undo();
943 assert_eq!(counter.load(Ordering::SeqCst), 0);
944 }
945
946 #[test]
947 fn test_execute_group() {
948 let mut history = CommandHistory::default();
949 let counter = Arc::new(AtomicI32::new(0));
950
951 let commands: Vec<Box<dyn Command>> = vec![
952 Box::new(IncrementCommand {
953 counter: counter.clone(),
954 amount: 1,
955 }),
956 Box::new(IncrementCommand {
957 counter: counter.clone(),
958 amount: 2,
959 }),
960 ];
961
962 let results = history.execute_group(commands);
963 assert!(results.iter().all(super::CommandResult::is_success));
964 assert_eq!(counter.load(Ordering::SeqCst), 3);
965 }
966
967 #[test]
968 fn test_checkpoints() {
969 let mut history = CommandHistory::default();
970 let counter = Arc::new(AtomicI32::new(0));
971
972 history.execute(Box::new(IncrementCommand {
973 counter: counter.clone(),
974 amount: 5,
975 }));
976
977 let checkpoint = history.create_checkpoint("Initial state");
978
979 history.execute(Box::new(IncrementCommand {
980 counter: counter.clone(),
981 amount: 10,
982 }));
983 history.execute(Box::new(IncrementCommand {
984 counter: counter.clone(),
985 amount: 15,
986 }));
987
988 assert_eq!(counter.load(Ordering::SeqCst), 30);
989
990 let restored = history.restore_checkpoint(checkpoint);
992 assert!(restored);
993 assert_eq!(counter.load(Ordering::SeqCst), 5);
994 }
995
996 #[test]
997 fn test_get_checkpoint() {
998 let mut history = CommandHistory::default();
999
1000 let id = history.create_checkpoint("Test checkpoint");
1001 let checkpoint = history.get_checkpoint(id);
1002
1003 assert!(checkpoint.is_some());
1004 assert_eq!(checkpoint.unwrap().name, "Test checkpoint");
1005 }
1006
1007 #[test]
1008 fn test_list_checkpoints() {
1009 let mut history = CommandHistory::default();
1010
1011 history.create_checkpoint("First");
1012 history.create_checkpoint("Second");
1013 history.create_checkpoint("Third");
1014
1015 assert_eq!(history.checkpoints().count(), 3);
1016 }
1017
1018 #[test]
1019 fn test_clear() {
1020 let mut history = CommandHistory::default();
1021 let counter = Arc::new(AtomicI32::new(0));
1022
1023 history.execute(Box::new(IncrementCommand {
1024 counter: counter.clone(),
1025 amount: 1,
1026 }));
1027 history.execute(Box::new(IncrementCommand { counter, amount: 2 }));
1028 history.create_checkpoint("Test");
1029
1030 history.clear();
1031
1032 assert!(!history.can_undo());
1033 assert!(!history.can_redo());
1034 assert_eq!(history.checkpoints().count(), 0);
1035 }
1036
1037 #[test]
1038 fn test_max_commands_limit() {
1039 let config = HistoryConfig {
1040 max_commands: 3,
1041 ..Default::default()
1042 };
1043 let mut history = CommandHistory::new(config);
1044 let counter = Arc::new(AtomicI32::new(0));
1045
1046 for i in 0..5 {
1047 history.execute(Box::new(IncrementCommand {
1048 counter: counter.clone(),
1049 amount: i + 1,
1050 }));
1051 }
1052
1053 assert_eq!(history.undo_count(), 3);
1054 }
1055
1056 #[test]
1057 fn test_pause_resume_recording() {
1058 let mut history = CommandHistory::default();
1059 let counter = Arc::new(AtomicI32::new(0));
1060
1061 history.execute(Box::new(IncrementCommand {
1062 counter: counter.clone(),
1063 amount: 1,
1064 }));
1065
1066 history.pause();
1067 assert!(!history.is_recording());
1068
1069 history.execute(Box::new(IncrementCommand {
1070 counter: counter.clone(),
1071 amount: 2,
1072 }));
1073
1074 assert_eq!(counter.load(Ordering::SeqCst), 3);
1076 assert_eq!(history.undo_count(), 1);
1077
1078 history.resume();
1079 assert!(history.is_recording());
1080
1081 history.execute(Box::new(IncrementCommand { counter, amount: 3 }));
1082 assert_eq!(history.undo_count(), 2);
1083 }
1084
1085 #[test]
1086 fn test_event_callbacks() {
1087 use std::sync::atomic::AtomicUsize;
1088
1089 let mut history = CommandHistory::default();
1090 let counter = Arc::new(AtomicI32::new(0));
1091 let event_count = Arc::new(AtomicUsize::new(0));
1092
1093 let ec = event_count.clone();
1094 history.on_event(Arc::new(move |_event| {
1095 ec.fetch_add(1, Ordering::SeqCst);
1096 }));
1097
1098 history.execute(Box::new(IncrementCommand { counter, amount: 1 }));
1099 history.undo();
1100 history.redo();
1101
1102 assert_eq!(event_count.load(Ordering::SeqCst), 3);
1103 }
1104
1105 #[test]
1106 fn test_memory_tracking() {
1107 let mut history = CommandHistory::default();
1108 let counter = Arc::new(AtomicI32::new(0));
1109
1110 assert_eq!(history.memory_usage(), 0);
1111
1112 history.execute(Box::new(IncrementCommand { counter, amount: 1 }));
1113
1114 assert!(history.memory_usage() > 0);
1115 }
1116
1117 #[test]
1118 fn test_tick() {
1119 let mut history = CommandHistory::default();
1120 history.tick(100);
1121 history.tick(50);
1122 }
1124
1125 #[test]
1126 fn test_command_result_helpers() {
1127 let success = CommandResult::Success;
1128 let failed = CommandResult::Failed("error".into());
1129 let cancelled = CommandResult::Cancelled;
1130
1131 assert!(success.is_success());
1132 assert!(!success.is_failed());
1133
1134 assert!(!failed.is_success());
1135 assert!(failed.is_failed());
1136
1137 assert!(!cancelled.is_success());
1138 assert!(!cancelled.is_failed());
1139 }
1140
1141 #[test]
1142 fn test_command_id() {
1143 let id = CommandId(42);
1144 assert_eq!(id.as_u64(), 42);
1145 }
1146
1147 #[test]
1148 fn test_group_id() {
1149 let id = GroupId(123);
1150 assert_eq!(id.as_u64(), 123);
1151 }
1152
1153 #[test]
1154 fn test_checkpoint_id() {
1155 let id = CheckpointId(456);
1156 assert_eq!(id.as_u64(), 456);
1157 }
1158
1159 #[test]
1160 fn test_composite_command() {
1161 let counter = Arc::new(AtomicI32::new(0));
1162
1163 let composite = CompositeCommand::new("Add 6")
1164 .with_command(Box::new(IncrementCommand {
1165 counter: counter.clone(),
1166 amount: 1,
1167 }))
1168 .with_command(Box::new(IncrementCommand {
1169 counter: counter.clone(),
1170 amount: 2,
1171 }))
1172 .with_command(Box::new(IncrementCommand {
1173 counter: counter.clone(),
1174 amount: 3,
1175 }))
1176 .build();
1177
1178 let mut history = CommandHistory::default();
1179 history.execute(composite);
1180
1181 assert_eq!(counter.load(Ordering::SeqCst), 6);
1182
1183 history.undo();
1184 assert_eq!(counter.load(Ordering::SeqCst), 0);
1185
1186 history.redo();
1187 assert_eq!(counter.load(Ordering::SeqCst), 6);
1188 }
1189
1190 #[test]
1191 fn test_composite_command_rollback_on_failure() {
1192 let counter = Arc::new(AtomicI32::new(0));
1193
1194 let composite = CompositeCommand::new("Should fail")
1195 .with_command(Box::new(IncrementCommand {
1196 counter: counter.clone(),
1197 amount: 5,
1198 }))
1199 .with_command(Box::new(FailingCommand { should_fail: true }))
1200 .build();
1201
1202 let mut history = CommandHistory::default();
1203 let result = history.execute(composite);
1204
1205 assert!(result.is_failed());
1206 assert_eq!(counter.load(Ordering::SeqCst), 0);
1208 }
1209
1210 #[test]
1211 fn test_set_value_command() {
1212 let value = Arc::new(std::sync::RwLock::new(10));
1213
1214 let cmd = Box::new(SetValueCommand::new("Set to 42", value.clone(), 42));
1215
1216 let mut history = CommandHistory::default();
1217 history.execute(cmd);
1218
1219 assert_eq!(*value.read().unwrap(), 42);
1220
1221 history.undo();
1222 assert_eq!(*value.read().unwrap(), 10);
1223
1224 history.redo();
1225 assert_eq!(*value.read().unwrap(), 42);
1226 }
1227
1228 #[test]
1229 fn test_default_config() {
1230 let config = HistoryConfig::default();
1231 assert_eq!(config.max_commands, 1000);
1232 assert_eq!(config.max_memory, 10 * 1024 * 1024);
1233 assert!(config.enable_merging);
1234 assert_eq!(config.auto_group_interval_ms, 500);
1235 }
1236
1237 #[test]
1238 fn test_undo_on_empty_returns_none() {
1239 let mut history = CommandHistory::default();
1240 assert!(history.undo().is_none());
1241 }
1242
1243 #[test]
1244 fn test_redo_on_empty_returns_none() {
1245 let mut history = CommandHistory::default();
1246 assert!(history.redo().is_none());
1247 }
1248
1249 #[test]
1250 fn test_restore_invalid_checkpoint() {
1251 let mut history = CommandHistory::default();
1252 let invalid_id = CheckpointId(999);
1253 assert!(!history.restore_checkpoint(invalid_id));
1254 }
1255
1256 #[test]
1257 fn test_history_event_variants() {
1258 let events = vec![
1259 HistoryEvent::Executed(CommandId(1)),
1260 HistoryEvent::Undone(CommandId(2)),
1261 HistoryEvent::Redone(CommandId(3)),
1262 HistoryEvent::Cleared,
1263 HistoryEvent::CheckpointCreated(CheckpointId(1)),
1264 HistoryEvent::CheckpointRestored(CheckpointId(1)),
1265 HistoryEvent::Trimmed(5),
1266 ];
1267
1268 for event in &events {
1270 assert_eq!(event, event);
1271 }
1272 }
1273
1274 #[test]
1275 fn test_redo_description() {
1276 let mut history = CommandHistory::default();
1277 let counter = Arc::new(AtomicI32::new(0));
1278
1279 assert!(history.redo_description().is_none());
1280
1281 history.execute(Box::new(IncrementCommand { counter, amount: 1 }));
1282 history.undo();
1283
1284 assert_eq!(history.redo_description(), Some("Increment counter"));
1285 }
1286
1287 #[test]
1288 fn test_redo_count() {
1289 let mut history = CommandHistory::default();
1290 let counter = Arc::new(AtomicI32::new(0));
1291
1292 assert_eq!(history.redo_count(), 0);
1293
1294 history.execute(Box::new(IncrementCommand {
1295 counter: counter.clone(),
1296 amount: 1,
1297 }));
1298 history.execute(Box::new(IncrementCommand { counter, amount: 2 }));
1299
1300 history.undo();
1301 assert_eq!(history.redo_count(), 1);
1302
1303 history.undo();
1304 assert_eq!(history.redo_count(), 2);
1305 }
1306
1307 #[test]
1308 fn test_composite_command_description() {
1309 let counter = Arc::new(AtomicI32::new(0));
1310
1311 let composite = CompositeCommand::new("My Composite")
1312 .with_command(Box::new(IncrementCommand { counter, amount: 1 }))
1313 .build();
1314
1315 assert_eq!(composite.description(), "My Composite");
1316 }
1317
1318 #[test]
1323 fn test_command_id_debug() {
1324 let id = CommandId(42);
1325 let debug = format!("{id:?}");
1326 assert!(debug.contains("CommandId"));
1327 assert!(debug.contains("42"));
1328 }
1329
1330 #[test]
1331 fn test_command_id_eq() {
1332 let id1 = CommandId(100);
1333 let id2 = CommandId(100);
1334 let id3 = CommandId(200);
1335 assert_eq!(id1, id2);
1336 assert_ne!(id1, id3);
1337 }
1338
1339 #[test]
1340 fn test_command_id_hash() {
1341 use std::collections::HashSet;
1342 let mut set = HashSet::new();
1343 set.insert(CommandId(1));
1344 set.insert(CommandId(2));
1345 set.insert(CommandId(1)); assert_eq!(set.len(), 2);
1347 }
1348
1349 #[test]
1350 fn test_group_id_debug() {
1351 let id = GroupId(99);
1352 let debug = format!("{id:?}");
1353 assert!(debug.contains("GroupId"));
1354 }
1355
1356 #[test]
1357 fn test_group_id_hash() {
1358 use std::collections::HashSet;
1359 let mut set = HashSet::new();
1360 set.insert(GroupId(1));
1361 set.insert(GroupId(2));
1362 assert_eq!(set.len(), 2);
1363 }
1364
1365 #[test]
1366 fn test_checkpoint_id_debug() {
1367 let id = CheckpointId(77);
1368 let debug = format!("{id:?}");
1369 assert!(debug.contains("CheckpointId"));
1370 }
1371
1372 #[test]
1373 fn test_checkpoint_id_hash() {
1374 use std::collections::HashSet;
1375 let mut set = HashSet::new();
1376 set.insert(CheckpointId(1));
1377 set.insert(CheckpointId(2));
1378 assert_eq!(set.len(), 2);
1379 }
1380
1381 #[test]
1382 fn test_command_result_debug() {
1383 let result = CommandResult::Success;
1384 let debug = format!("{result:?}");
1385 assert!(debug.contains("Success"));
1386
1387 let failed = CommandResult::Failed("error".to_string());
1388 let debug = format!("{failed:?}");
1389 assert!(debug.contains("Failed"));
1390 }
1391
1392 #[test]
1393 fn test_command_result_clone() {
1394 let result = CommandResult::NeedsConfirmation("confirm?".to_string());
1395 let cloned = result.clone();
1396 assert_eq!(result, cloned);
1397 }
1398
1399 #[test]
1400 fn test_command_result_all_variants() {
1401 let variants = vec![
1402 CommandResult::Success,
1403 CommandResult::Failed("error".to_string()),
1404 CommandResult::Cancelled,
1405 CommandResult::NeedsConfirmation("confirm".to_string()),
1406 ];
1407
1408 for variant in &variants {
1409 let _ = format!("{variant:?}");
1410 let cloned = variant.clone();
1411 assert_eq!(variant, &cloned);
1412 }
1413 }
1414
1415 #[test]
1416 fn test_history_config_debug() {
1417 let config = HistoryConfig::default();
1418 let debug = format!("{config:?}");
1419 assert!(debug.contains("HistoryConfig"));
1420 }
1421
1422 #[test]
1423 fn test_history_config_clone() {
1424 let config = HistoryConfig {
1425 max_commands: 500,
1426 max_memory: 5 * 1024 * 1024,
1427 enable_merging: false,
1428 auto_group_interval_ms: 1000,
1429 };
1430 let cloned = config;
1431 assert_eq!(cloned.max_commands, 500);
1432 assert!(!cloned.enable_merging);
1433 }
1434
1435 #[test]
1436 fn test_checkpoint_debug() {
1437 let checkpoint = Checkpoint {
1438 id: CheckpointId(1),
1439 name: "Test".to_string(),
1440 position: 5,
1441 timestamp: 1000,
1442 };
1443 let debug = format!("{checkpoint:?}");
1444 assert!(debug.contains("Checkpoint"));
1445 }
1446
1447 #[test]
1448 fn test_checkpoint_clone() {
1449 let checkpoint = Checkpoint {
1450 id: CheckpointId(1),
1451 name: "Test".to_string(),
1452 position: 5,
1453 timestamp: 1000,
1454 };
1455 let cloned = checkpoint;
1456 assert_eq!(cloned.name, "Test");
1457 assert_eq!(cloned.position, 5);
1458 }
1459
1460 #[test]
1461 fn test_history_event_debug() {
1462 let event = HistoryEvent::Cleared;
1463 let debug = format!("{event:?}");
1464 assert!(debug.contains("Cleared"));
1465 }
1466
1467 #[test]
1468 fn test_history_event_clone() {
1469 let event = HistoryEvent::Trimmed(10);
1470 let cloned = event.clone();
1471 assert_eq!(event, cloned);
1472 }
1473
1474 #[test]
1475 fn test_memory_limit_trimming() {
1476 let config = HistoryConfig {
1477 max_commands: 1000,
1478 max_memory: 200, ..Default::default()
1480 };
1481 let mut history = CommandHistory::new(config);
1482 let counter = Arc::new(AtomicI32::new(0));
1483
1484 for i in 0..10 {
1486 history.execute(Box::new(IncrementCommand {
1487 counter: counter.clone(),
1488 amount: i,
1489 }));
1490 }
1491
1492 assert!(history.undo_count() < 10);
1494 }
1495
1496 #[test]
1497 fn test_default_command_methods() {
1498 let counter = Arc::new(AtomicI32::new(0));
1499 let cmd = IncrementCommand { counter, amount: 1 };
1500
1501 assert!(!cmd.can_merge(&cmd));
1503 assert_eq!(cmd.memory_size(), 64);
1504 assert!(cmd.metadata().is_none());
1505 }
1506
1507 #[test]
1508 fn test_composite_command_memory_size() {
1509 let counter = Arc::new(AtomicI32::new(0));
1510
1511 let composite = CompositeCommand::new("Multi")
1512 .with_command(Box::new(IncrementCommand {
1513 counter: counter.clone(),
1514 amount: 1,
1515 }))
1516 .with_command(Box::new(IncrementCommand { counter, amount: 2 }))
1517 .build();
1518
1519 let memory = composite.memory_size();
1520 assert!(memory > 64); }
1522
1523 #[test]
1524 fn test_group_redo() {
1525 let mut history = CommandHistory::default();
1526 let counter = Arc::new(AtomicI32::new(0));
1527
1528 let _group = history.begin_group();
1529 history.execute(Box::new(IncrementCommand {
1530 counter: counter.clone(),
1531 amount: 1,
1532 }));
1533 history.execute(Box::new(IncrementCommand {
1534 counter: counter.clone(),
1535 amount: 2,
1536 }));
1537 history.end_group();
1538
1539 assert_eq!(counter.load(Ordering::SeqCst), 3);
1540
1541 history.undo();
1542 assert_eq!(counter.load(Ordering::SeqCst), 0);
1543
1544 history.redo();
1545 assert_eq!(counter.load(Ordering::SeqCst), 3);
1546 }
1547
1548 #[test]
1549 fn test_checkpoint_position_updates_on_trim() {
1550 let config = HistoryConfig {
1551 max_commands: 3,
1552 ..Default::default()
1553 };
1554 let mut history = CommandHistory::new(config);
1555 let counter = Arc::new(AtomicI32::new(0));
1556
1557 history.execute(Box::new(IncrementCommand {
1558 counter: counter.clone(),
1559 amount: 1,
1560 }));
1561
1562 let checkpoint_id = history.create_checkpoint("Early");
1563
1564 for i in 2..=5 {
1566 history.execute(Box::new(IncrementCommand {
1567 counter: counter.clone(),
1568 amount: i,
1569 }));
1570 }
1571
1572 let checkpoint = history.get_checkpoint(checkpoint_id);
1574 assert!(checkpoint.is_some());
1575 }
1576
1577 #[test]
1578 fn test_multiple_event_listeners() {
1579 use std::sync::atomic::AtomicUsize;
1580
1581 let mut history = CommandHistory::default();
1582 let counter = Arc::new(AtomicI32::new(0));
1583 let event_count1 = Arc::new(AtomicUsize::new(0));
1584 let event_count2 = Arc::new(AtomicUsize::new(0));
1585
1586 let ec1 = event_count1.clone();
1587 history.on_event(Arc::new(move |_| {
1588 ec1.fetch_add(1, Ordering::SeqCst);
1589 }));
1590
1591 let ec2 = event_count2.clone();
1592 history.on_event(Arc::new(move |_| {
1593 ec2.fetch_add(1, Ordering::SeqCst);
1594 }));
1595
1596 history.execute(Box::new(IncrementCommand { counter, amount: 1 }));
1597
1598 assert_eq!(event_count1.load(Ordering::SeqCst), 1);
1599 assert_eq!(event_count2.load(Ordering::SeqCst), 1);
1600 }
1601
1602 #[test]
1603 fn test_set_value_command_description() {
1604 let value = Arc::new(std::sync::RwLock::new(0));
1605 let cmd = SetValueCommand::new("Set value", value, 100);
1606 assert_eq!(cmd.description(), "Set value");
1607 }
1608
1609 #[test]
1610 fn test_undo_without_old_value() {
1611 let value = Arc::new(std::sync::RwLock::new(10));
1612 let mut cmd = SetValueCommand::new("Set", value, 42);
1613
1614 let result = cmd.undo();
1616 assert!(result.is_failed());
1617 }
1618
1619 #[test]
1620 fn test_empty_composite_command() {
1621 let composite = CompositeCommand::new("Empty").build();
1622
1623 let mut history = CommandHistory::default();
1624 let result = history.execute(composite);
1625
1626 assert!(result.is_success());
1627 }
1628
1629 #[test]
1630 fn test_get_nonexistent_checkpoint() {
1631 let history = CommandHistory::default();
1632 let result = history.get_checkpoint(CheckpointId(999));
1633 assert!(result.is_none());
1634 }
1635
1636 #[test]
1637 fn test_history_clear_resets_memory() {
1638 let mut history = CommandHistory::default();
1639 let counter = Arc::new(AtomicI32::new(0));
1640
1641 history.execute(Box::new(IncrementCommand { counter, amount: 1 }));
1642
1643 assert!(history.memory_usage() > 0);
1644
1645 history.clear();
1646 assert_eq!(history.memory_usage(), 0);
1647 }
1648
1649 struct MergeableIncrement {
1651 counter: Arc<AtomicI32>,
1652 total_amount: i32,
1653 }
1654
1655 impl Command for MergeableIncrement {
1656 fn execute(&mut self) -> CommandResult {
1657 self.counter.fetch_add(self.total_amount, Ordering::SeqCst);
1658 CommandResult::Success
1659 }
1660
1661 fn undo(&mut self) -> CommandResult {
1662 self.counter.fetch_sub(self.total_amount, Ordering::SeqCst);
1663 CommandResult::Success
1664 }
1665
1666 fn description(&self) -> &'static str {
1667 "Mergeable increment"
1668 }
1669
1670 fn can_merge(&self, _other: &dyn Command) -> bool {
1671 true
1672 }
1673
1674 fn merge(&mut self, _other: Box<dyn Command>) -> Option<Box<dyn Command>> {
1675 self.total_amount += 10;
1677 None
1678 }
1679 }
1680
1681 #[test]
1682 fn test_command_merging() {
1683 let config = HistoryConfig {
1684 enable_merging: true,
1685 auto_group_interval_ms: 0, ..Default::default()
1687 };
1688 let mut history = CommandHistory::new(config);
1689 let counter = Arc::new(AtomicI32::new(0));
1690
1691 history.execute(Box::new(MergeableIncrement {
1692 counter: counter.clone(),
1693 total_amount: 5,
1694 }));
1695
1696 history.execute(Box::new(MergeableIncrement {
1697 counter,
1698 total_amount: 3,
1699 }));
1700
1701 assert_eq!(history.undo_count(), 1);
1703 }
1704
1705 #[test]
1706 fn test_redo_clears_on_new_execute() {
1707 let mut history = CommandHistory::default();
1708 let counter = Arc::new(AtomicI32::new(0));
1709
1710 history.execute(Box::new(IncrementCommand {
1711 counter: counter.clone(),
1712 amount: 1,
1713 }));
1714
1715 history.undo();
1716 assert_eq!(history.redo_count(), 1);
1717
1718 history.execute(Box::new(IncrementCommand { counter, amount: 2 }));
1719
1720 assert_eq!(history.redo_count(), 0);
1721 }
1722}