use alloc::vec::Vec;
pub trait Revert<T> {
fn revert(self, target: &mut T) -> Self;
}
impl<T> Revert<T> for () {
fn revert(self, _target: &mut T) -> Self {
()
}
}
pub trait Extract<T> {
fn extract(target: &mut T) -> Self;
}
impl<T> Extract<T> for () {
fn extract(_target: &mut T) -> Self {
()
}
}
pub struct CmdEdit<Cmd, E> {
pub cmd: Cmd,
pub edit: E,
}
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: Extract<T>,
{
self.cmd_commit(Default::default(), E::extract(target));
}
}
impl<Cmd, E> UndoRedo<E, Cmd> {
pub fn cmd_commit(&mut self, cmd: Cmd, edit: E) {
self.done.push(CmdEdit { cmd, edit });
self.undone.clear();
}
}
impl<Cmd> UndoRedo<(), Cmd> {
pub fn command(&mut self, command: Cmd) {
self.cmd_commit(command, ());
}
}
impl<Cmd: Clone, E: Clone> UndoRedo<E, Cmd> {
pub fn undo<T>(&mut self, target: &mut T) -> Option<Cmd>
where
E: Revert<T>,
{
let CmdEdit { cmd, edit } = self.done.pop()?;
self.undone.push(CmdEdit {
cmd: cmd.clone(),
edit: edit.revert(target),
});
Some(cmd)
}
pub fn redo<T>(&mut self, target: &mut T) -> Option<Cmd>
where
E: Revert<T>,
{
let CmdEdit { cmd, edit } = self.undone.pop()?;
self.done.push(CmdEdit {
cmd: cmd.clone(),
edit: edit.revert(target),
});
Some(cmd)
}
}