logisheets_controller/version_manager/
mod.rs1pub mod ctx;
2pub mod diff;
3
4use std::collections::{HashMap, HashSet, VecDeque};
5
6use logisheets_base::{CellId, SheetId};
7
8use crate::{controller::status::Status, edit_action::PayloadsAction};
9
10use self::diff::{convert_payloads_to_sheet_diff, Diff, SheetDiff};
11
12const HISTORY_SIZE: usize = 50;
13
14#[derive(Debug, Default)]
17pub struct VersionManager {
18 version: u32,
19 undo_stack: VecDeque<Status>,
20 redo_stack: VecDeque<Status>,
21 diff_undo_stack: VecDeque<HashMap<SheetId, SheetDiff>>,
22 diff_redo_stack: VecDeque<HashMap<SheetId, SheetDiff>>,
23 current_status: Status,
24 current_diffs: HashMap<SheetId, SheetDiff>,
25}
26
27impl VersionManager {
28 pub fn version(&self) -> u32 {
29 self.version
30 }
31
32 pub fn record(
33 &mut self,
34 mut status: Status,
35 processes: PayloadsAction,
36 updated_cells: HashSet<(SheetId, CellId)>,
37 ) {
38 let diffs = convert_payloads_to_sheet_diff(&mut status, processes, updated_cells);
39 self.add_status(status, diffs);
40 self.version += 1;
41 }
42
43 fn add_status(&mut self, current: Status, sheet_diff: HashMap<SheetId, SheetDiff>) {
44 self.redo_stack.clear();
45 self.diff_redo_stack.clear();
46
47 if self.undo_stack.len() >= HISTORY_SIZE {
48 self.undo_stack.pop_front();
49 self.diff_undo_stack.pop_front();
50 }
51
52 let mut current = current;
53 let mut sheet_diff = sheet_diff;
54
55 std::mem::swap(&mut current, &mut self.current_status);
56 std::mem::swap(&mut sheet_diff, &mut self.current_diffs);
57
58 self.undo_stack.push_back(current);
59 self.diff_undo_stack.push_back(sheet_diff);
60 }
61
62 pub fn undo(&mut self) -> Option<Status> {
63 let mut status = self.undo_stack.pop_back()?;
64 let mut payloads = self.diff_undo_stack.pop_back()?;
65
66 std::mem::swap(&mut status, &mut self.current_status);
67 std::mem::swap(&mut payloads, &mut self.current_diffs);
68
69 self.redo_stack.push_back(status.clone());
70 self.diff_redo_stack.push_back(payloads);
71
72 Some(status)
73 }
74
75 pub fn redo(&mut self) -> Option<Status> {
76 let mut status = self.redo_stack.pop_back()?;
77 let mut payloads = self.diff_redo_stack.pop_back()?;
78
79 std::mem::swap(&mut status, &mut self.current_status);
80 std::mem::swap(&mut payloads, &mut self.current_diffs);
81
82 self.undo_stack.push_back(status.clone());
83 self.diff_undo_stack.push_back(payloads);
84
85 Some(status)
86 }
87
88 pub fn get_sheet_diffs_from_version(&self, sheet: SheetId, version: u32) -> Option<SheetDiff> {
90 if version > self.version {
91 return None;
92 }
93
94 if self.version - HISTORY_SIZE as u32 >= version {
95 return None;
96 }
97
98 let start_idx = if self.version >= 50 {
99 HISTORY_SIZE - 1 + (version - self.version) as usize
100 } else {
101 version as usize
102 };
103
104 let mut result: HashSet<Diff> = HashSet::new();
105
106 for i in start_idx..self.undo_stack.len() {
107 let sheet_diffs = self.diff_undo_stack.get(i)?;
108 if let Some(diff) = sheet_diffs.get(&sheet) {
109 if diff.diff_unavailable() {
110 return None;
111 }
112 result.extend(diff.data.clone())
113 }
114 }
115
116 Some(SheetDiff { data: result })
117 }
118}