undo/history/
checkpoint.rs

1use crate::{At, Edit, History, Slot};
2use alloc::vec::Vec;
3
4#[derive(Debug)]
5enum CheckpointEntry {
6    Edit(usize),
7    Undo,
8    Redo,
9}
10
11/// Wraps a [`History`] and gives it checkpoint functionality.
12#[derive(Debug)]
13pub struct Checkpoint<'a, E, S> {
14    history: &'a mut History<E, S>,
15    entries: Vec<CheckpointEntry>,
16}
17
18impl<E, S> Checkpoint<'_, E, S> {
19    /// Reserves capacity for at least `additional` more entries in the checkpoint.
20    ///
21    /// # Panics
22    /// Panics if the new capacity exceeds `isize::MAX` bytes.
23    pub fn reserve(&mut self, additional: usize) {
24        self.entries.reserve(additional);
25    }
26
27    /// Commits the changes and consumes the checkpoint.
28    pub fn commit(self) {}
29}
30
31impl<E: Edit, S: Slot> Checkpoint<'_, E, S> {
32    /// Calls the [`History::edit`] method.
33    pub fn edit(&mut self, target: &mut E::Target, edit: E) -> E::Output {
34        self.entries.push(CheckpointEntry::Edit(self.history.root));
35        self.history.edit(target, edit)
36    }
37
38    /// Calls the [`History::undo`] method.
39    pub fn undo(&mut self, target: &mut E::Target) -> Option<E::Output> {
40        self.entries.push(CheckpointEntry::Undo);
41        self.history.undo(target)
42    }
43
44    /// Calls the [`History::redo`] method.
45    pub fn redo(&mut self, target: &mut E::Target) -> Option<E::Output> {
46        self.entries.push(CheckpointEntry::Redo);
47        self.history.redo(target)
48    }
49
50    /// Cancels the changes and consumes the checkpoint.
51    pub fn cancel(self, target: &mut E::Target) -> Vec<E::Output> {
52        self.entries
53            .into_iter()
54            .rev()
55            .filter_map(|entry| match entry {
56                CheckpointEntry::Edit(root) => {
57                    let output = self.history.undo(target)?;
58                    if self.history.root == root {
59                        self.history.record.entries.pop_back();
60                    } else {
61                        // If a new root was created when we edited earlier,
62                        // we remove it and append the entries to the previous root.
63                        let mut branch = self.history.branches.remove(root);
64                        debug_assert_eq!(branch.parent, self.history.head());
65
66                        let new = At::new(root, self.history.record.head());
67                        let (_, rm_saved) = self.history.record.rm_tail();
68                        self.history.record.entries.append(&mut branch.entries);
69                        self.history.set_root(new, rm_saved);
70                    }
71                    Some(output)
72                }
73                CheckpointEntry::Undo => self.history.redo(target),
74                CheckpointEntry::Redo => self.history.undo(target),
75            })
76            .collect()
77    }
78}
79
80impl<'a, E, S> From<&'a mut History<E, S>> for Checkpoint<'a, E, S> {
81    fn from(history: &'a mut History<E, S>) -> Self {
82        Checkpoint {
83            history,
84            entries: Vec::new(),
85        }
86    }
87}