use alloc::vec::Vec;
use maplike::{Get, Insert, KeyedCollection};
use crate::{Edit, Recorder, edit::ApplyEdit};
pub struct CmdEdit<Cmd, EC> {
pub cmd: Cmd,
pub edit: Edit<EC>,
}
pub struct UndoRedo<EC, Cmd = ()> {
done: Vec<CmdEdit<Cmd, EC>>,
undone: Vec<CmdEdit<Cmd, EC>>,
}
impl<Cmd, EC> UndoRedo<EC, Cmd> {
pub fn new() -> Self {
Self {
done: Vec::new(),
undone: Vec::new(),
}
}
pub fn done(&self) -> &[CmdEdit<Cmd, EC>] {
&self.done
}
pub fn undone(&self) -> &[CmdEdit<Cmd, EC>] {
&self.undone
}
}
impl<Cmd: Default, EC> UndoRedo<EC, Cmd> {
pub fn commit(&mut self, edit: Edit<EC>) {
self.cmd_commit(Default::default(), edit);
}
}
impl<Cmd, EC> UndoRedo<EC, Cmd> {
pub fn cmd_commit(&mut self, cmd: Cmd, edit: Edit<EC>) {
self.done.push(CmdEdit { cmd, edit });
self.undone.clear();
}
}
impl<Cmd, EC: KeyedCollection + Default> UndoRedo<EC, Cmd> {
pub fn edit<
K,
V,
C: KeyedCollection<Key = K, Value = V> + Get<K> + Insert<K>,
F: FnOnce(&mut Recorder<C, EC>) -> Cmd,
>(
&mut self,
collection: C,
f: F,
) -> C
where
EC: KeyedCollection<Key = K, Value = V>,
K: Clone,
V: Clone,
{
let mut recorder = Recorder::<C, EC>::new(collection);
let cmd = f(&mut recorder);
let (container, edit) = recorder.dissolve();
self.cmd_commit(cmd, edit);
container
}
}
impl<Cmd: Clone, EC: Clone> UndoRedo<EC, Cmd> {
pub fn undo(&mut self, target: &mut impl ApplyEdit<EC>) -> Option<Cmd> {
let CmdEdit { cmd, edit } = self.done.pop()?;
let reverse_edit = edit.reverse();
target.apply_edit(&reverse_edit);
self.undone.push(CmdEdit {
cmd: cmd.clone(),
edit: reverse_edit,
});
Some(cmd)
}
pub fn redo(&mut self, target: &mut impl ApplyEdit<EC>) -> Option<Cmd> {
let CmdEdit { cmd, edit } = self.undone.pop()?;
let reverse_edit = edit.reverse();
target.apply_edit(&reverse_edit);
self.done.push(CmdEdit {
cmd: cmd.clone(),
edit: reverse_edit,
});
Some(cmd)
}
}