use maplike::{Container, Get};
use std::vec::Vec;
use crate::{CmdEdit, Delta, ExtractEdit, Recorder, RevertEdit};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct UndoRedo<E, Cmd = ()> {
done: Vec<CmdEdit<Cmd, E>>,
undone: Vec<CmdEdit<Cmd, E>>,
}
impl<Cmd, E> UndoRedo<E, Cmd> {
#[inline]
pub fn new() -> Self {
Self {
done: Vec::new(),
undone: Vec::new(),
}
}
#[inline]
pub fn done(&self) -> &[CmdEdit<Cmd, E>] {
&self.done
}
#[inline]
pub fn undone(&self) -> &[CmdEdit<Cmd, E>] {
&self.undone
}
}
impl<Cmd: Default, E> UndoRedo<E, Cmd> {
#[inline]
pub fn commit<T>(&mut self, target: &mut T)
where
E: ExtractEdit<T>,
{
self.cmd_commit(Default::default(), target);
}
}
impl<Cmd, E> UndoRedo<E, Cmd> {
#[inline]
pub fn cmd_commit<T>(&mut self, cmd: Cmd, target: &mut T)
where
E: ExtractEdit<T>,
{
self.done.push(CmdEdit {
cmd,
edit: E::extract_edit(target),
});
self.undone.clear();
}
}
impl<Cmd> UndoRedo<(), Cmd> {
#[inline]
pub fn command(&mut self, command: Cmd) {
self.cmd_commit(command, &mut ());
}
}
impl<Cmd: Clone, E: Clone> UndoRedo<E, Cmd> {
#[inline]
pub fn undo<T>(&mut self, target: &mut T) -> Option<Cmd>
where
E: RevertEdit<T>,
{
let CmdEdit { cmd, edit } = self.done.pop()?;
self.undone.push(CmdEdit {
cmd: cmd.clone(),
edit: edit.revert_edit(target),
});
Some(cmd)
}
#[inline]
pub fn redo<T>(&mut self, target: &mut T) -> Option<Cmd>
where
E: RevertEdit<T>,
{
let CmdEdit { cmd, edit } = self.undone.pop()?;
self.done.push(CmdEdit {
cmd: cmd.clone(),
edit: edit.revert_edit(target),
});
Some(cmd)
}
}
impl<Cmd: Clone> UndoRedo<(), Cmd> {
#[inline]
pub fn undo_command(&mut self) -> Option<Cmd> {
let CmdEdit { cmd, .. } = self.done.pop()?;
self.undone.push(CmdEdit {
cmd: cmd.clone(),
edit: (),
});
Some(cmd)
}
#[inline]
pub fn redo_command(&mut self) -> Option<Cmd> {
let CmdEdit { cmd, .. } = self.undone.pop()?;
self.done.push(CmdEdit {
cmd: cmd.clone(),
edit: (),
});
Some(cmd)
}
}
impl<Cmd, DC: Container + Default> UndoRedo<Delta<DC>, Cmd> {
#[inline]
pub fn edit<K, V, C, F>(&mut self, container: C, f: F) -> C
where
C: Container<Key = K, Value = V> + Get<K>,
DC: Container<Key = K, Value = V>,
K: Clone,
V: Clone,
F: FnOnce(&mut Recorder<C, DC>) -> Cmd,
{
let mut recorder = Recorder::<C, DC>::new(container);
let cmd = f(&mut recorder);
let (container, delta) = recorder.dissolve();
self.done.push(CmdEdit { cmd, edit: delta });
self.undone.clear();
container
}
}