use alloc::vec::Vec;
use maplike::{Container, Get};
use crate::{Delta, Recorder};
pub trait RevertEdit<T> {
fn revert_edit(self, target: &mut T) -> Self;
}
impl<T> RevertEdit<T> for () {
fn revert_edit(self, _target: &mut T) -> Self {
()
}
}
pub trait ExtractEdit<T> {
fn extract_edit(target: &mut T) -> Self;
}
impl<T> ExtractEdit<T> for () {
fn extract_edit(_target: &mut T) -> Self {
()
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct CmdEdit<Cmd, E> {
pub cmd: Cmd,
pub edit: E,
}
#[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> {
pub fn new() -> Self {
Self {
done: Vec::new(),
undone: Vec::new(),
}
}
pub fn done(&self) -> &[CmdEdit<Cmd, E>] {
&self.done
}
pub fn undone(&self) -> &[CmdEdit<Cmd, E>] {
&self.undone
}
}
impl<Cmd: Default, E> UndoRedo<E, Cmd> {
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> {
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> {
pub fn command(&mut self, command: Cmd) {
self.cmd_commit(command, &mut ());
}
}
impl<Cmd: Clone, E: Clone> UndoRedo<E, Cmd> {
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)
}
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, DC: Container + Default> UndoRedo<Delta<DC>, Cmd> {
pub fn edit<
K,
V,
C: Container<Key = K, Value = V> + Get<K>,
F: FnOnce(&mut Recorder<C, DC>) -> Cmd,
>(
&mut self,
container: C,
f: F,
) -> C
where
DC: Container<Key = K, Value = V>,
K: Clone,
V: Clone,
{
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
}
}