blockworx_tools/history/
undoer.rs1use core::time::Duration;
14use std::collections::VecDeque;
15
16#[derive(Clone)]
17pub struct Settings {
18 pub max_undos: usize,
20 pub stable_time: Duration,
22 pub auto_save_interval: Duration,
25}
26
27impl Default for Settings {
28 fn default() -> Self {
29 Self {
30 max_undos: 100,
31 stable_time: Duration::from_secs(1),
32 auto_save_interval: Duration::from_secs(30),
33 }
34 }
35}
36
37#[derive(Clone)]
40struct Flux<State> {
41 started: Duration,
42 changed: Duration,
43 latest: State,
44}
45
46#[derive(Clone)]
47pub struct Undoer<State> {
48 settings: Settings,
49 undos: VecDeque<State>,
52 redos: Vec<State>,
54 flux: Option<Flux<State>>,
55}
56
57impl<State: Clone + PartialEq> Undoer<State> {
58 pub fn with_settings(settings: Settings) -> Self {
59 Self {
60 settings,
61 undos: VecDeque::new(),
62 redos: Vec::new(),
63 flux: None,
64 }
65 }
66
67 pub fn has_undo(&self, current: &State) -> bool {
69 match self.undos.len() {
70 0 => false,
71 1 => self.undos.back() != Some(current),
72 _ => true,
73 }
74 }
75
76 pub fn undo(&mut self, current: &State) -> Option<&State> {
77 if !self.has_undo(current) {
78 return None;
79 }
80 self.flux = None;
81 if let Some(state) = self.undos.pop_back_if(|state| state == current) {
82 self.redos.push(state);
83 } else {
84 self.redos.push(current.clone());
85 }
86 self.undos.back()
87 }
88
89 pub fn redo(&mut self, current: &State) -> Option<&State> {
90 if !self.undos.is_empty() && self.undos.back() != Some(current) {
91 self.redos.clear();
94 return None;
95 }
96 let state = self.redos.pop()?;
97 self.undos.push_back(state);
98 self.undos.back()
99 }
100
101 pub fn add_undo(&mut self, current: &State) {
103 if self.undos.back() != Some(current) {
104 self.undos.push_back(current.clone());
105 }
106 while self.undos.len() > self.settings.max_undos {
107 self.undos.pop_front();
108 }
109 self.flux = None;
110 }
111
112 pub fn feed_state(&mut self, now: Duration, current: &State) {
115 let Some(latest) = self.undos.back() else {
116 self.add_undo(current);
117 return;
118 };
119 if latest == current {
120 self.flux = None;
121 return;
122 }
123 self.redos.clear();
124 let Some(flux) = self.flux.as_mut() else {
125 self.flux = Some(Flux {
126 started: now,
127 changed: now,
128 latest: current.clone(),
129 });
130 return;
131 };
132 if &flux.latest == current {
133 if now.saturating_sub(flux.changed) >= self.settings.stable_time {
134 self.add_undo(current);
135 }
136 } else if now.saturating_sub(flux.started) >= self.settings.auto_save_interval {
137 self.add_undo(current);
138 } else {
139 flux.changed = now;
140 flux.latest = current.clone();
141 }
142 }
143}