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