use std::mem;
pub enum Change {
Insert(usize, u8),
Remove(usize, u8),
}
impl Change {
pub fn reverse(self) -> Change {
match self {
Change::Insert(usize, u8) => Change::Remove(usize, u8),
Change::Remove(usize, u8) => Change::Insert(usize, u8),
}
}
}
pub struct LogEntry {
init_point: usize,
pub end_point: usize,
pub changes: Vec<Change>,
}
impl LogEntry {
pub fn reverse(mut self) -> LogEntry {
self.changes.reverse();
LogEntry {
init_point: self.end_point,
end_point: self.init_point,
changes: self.changes.into_iter().map( |change| change.reverse() ).collect(),
}
}
}
pub struct Transaction<'a> {
entries: &'a mut Log,
entry: LogEntry,
}
impl<'a> Transaction<'a> {
pub fn log(&mut self, change: Change, idx: usize) {
self.entry.changes.push(change);
self.entry.end_point = idx;
}
}
impl<'a> Drop for Transaction<'a> {
fn drop(&mut self) {
if self.entry.changes.is_empty() { return }
let entry = LogEntry {
changes: mem::replace(&mut self.entry.changes, Vec::new()),
.. self.entry
};
self.entries.undo.push(entry);
self.entries.redo.clear();
}
}
pub struct Log {
undo: Vec<LogEntry>,
redo: Vec<LogEntry>,
}
impl Log {
pub fn new() -> Log {
Log {
undo: Vec::new(),
redo: Vec::new(),
}
}
pub fn start<'a, 'b>(&'a mut self, idx: usize) -> Transaction<'a> {
Transaction {
entries: self,
entry: LogEntry {
init_point: idx,
end_point: idx,
changes: Vec::new(),
}
}
}
pub fn undo(&mut self) -> Option<&LogEntry> {
match self.undo.pop() {
Some(change) => {
let last = self.redo.len();
self.redo.push(change.reverse());
Some(&self.redo[last])
},
None => None
}
}
pub fn redo(&mut self) -> Option<&LogEntry> {
match self.redo.pop() {
Some(change) => {
let last = self.undo.len();
self.undo.push(change.reverse());
Some(&self.undo[last])
},
None => None
}
}
}