jellyflow_core/ops/history/
store.rs1use crate::ops::{GraphTransaction, normalize_transaction};
2
3pub const DEFAULT_HISTORY_LIMIT: usize = 256;
5
6#[derive(Debug, Clone)]
8pub struct GraphHistory {
9 limit: usize,
10 undo: Vec<GraphTransaction>,
11 redo: Vec<GraphTransaction>,
12}
13
14impl Default for GraphHistory {
15 fn default() -> Self {
16 Self::new(DEFAULT_HISTORY_LIMIT)
17 }
18}
19
20impl GraphHistory {
21 pub fn new(limit: usize) -> Self {
22 Self {
23 limit: limit.max(1),
24 undo: Vec::new(),
25 redo: Vec::new(),
26 }
27 }
28
29 pub fn clear(&mut self) {
30 self.undo.clear();
31 self.redo.clear();
32 }
33
34 pub fn can_undo(&self) -> bool {
35 !self.undo.is_empty()
36 }
37
38 pub fn can_redo(&self) -> bool {
39 !self.redo.is_empty()
40 }
41
42 pub fn undo_len(&self) -> usize {
43 self.undo.len()
44 }
45
46 pub fn redo_len(&self) -> usize {
47 self.redo.len()
48 }
49
50 pub fn record(&mut self, tx: GraphTransaction) {
52 let tx = normalize_transaction(tx);
53 if tx.is_empty() {
54 return;
55 }
56 self.undo.push(tx);
57 self.redo.clear();
58 if self.undo.len() > self.limit {
59 let overflow = self.undo.len() - self.limit;
60 self.undo.drain(0..overflow);
61 }
62 }
63
64 pub fn undo<E>(
69 &mut self,
70 mut apply: impl FnMut(&GraphTransaction) -> Result<GraphTransaction, E>,
71 ) -> Result<bool, E> {
72 let Some(tx) = self.undo.pop() else {
73 return Ok(false);
74 };
75
76 let inverse = tx.inverse();
77 match apply(&inverse) {
78 Ok(committed) => {
79 let redo_tx = normalize_transaction(committed.inverse());
80 if !redo_tx.is_empty() {
81 self.redo.push(redo_tx);
82 }
83 Ok(true)
84 }
85 Err(err) => {
86 self.undo.push(tx);
87 Err(err)
88 }
89 }
90 }
91
92 pub fn redo<E>(
94 &mut self,
95 mut apply: impl FnMut(&GraphTransaction) -> Result<GraphTransaction, E>,
96 ) -> Result<bool, E> {
97 let Some(tx) = self.redo.pop() else {
98 return Ok(false);
99 };
100
101 match apply(&tx) {
102 Ok(committed) => {
103 let committed = normalize_transaction(committed);
104 if !committed.is_empty() {
105 self.undo.push(committed);
106 }
107 Ok(true)
108 }
109 Err(err) => {
110 self.redo.push(tx);
111 Err(err)
112 }
113 }
114 }
115}