use crate::{Command, History, Meta, Queue, Record, Result};
use std::collections::VecDeque;
#[derive(Debug)]
enum Action<R> {
Apply(VecDeque<Meta<R>>),
Undo,
Redo,
GoTo(usize, usize),
}
#[derive(Debug)]
pub struct Checkpoint<'a, T, R> {
inner: &'a mut T,
stack: Vec<Action<R>>,
}
impl<'a, T, R> From<&'a mut T> for Checkpoint<'a, T, R> {
#[inline]
fn from(inner: &'a mut T) -> Self {
Checkpoint {
inner,
stack: Vec::new(),
}
}
}
impl<R> Checkpoint<'_, Record<R>, R> {
#[inline]
pub fn apply(&mut self, command: impl Command<R> + 'static) -> Result<R>
where
R: 'static,
{
let (_, v) = self.inner.__apply(Meta::new(command))?;
self.stack.push(Action::Apply(v));
Ok(())
}
#[inline]
#[must_use]
pub fn undo(&mut self) -> Option<Result<R>> {
match self.inner.undo() {
Some(Ok(_)) => {
self.stack.push(Action::Undo);
Some(Ok(()))
}
undo => undo,
}
}
#[inline]
#[must_use]
pub fn redo(&mut self) -> Option<Result<R>> {
match self.inner.redo() {
Some(Ok(_)) => {
self.stack.push(Action::Redo);
Some(Ok(()))
}
redo => redo,
}
}
#[inline]
#[must_use]
pub fn go_to(&mut self, cursor: usize) -> Option<Result<R>> {
let old = self.inner.cursor();
match self.inner.go_to(cursor) {
Some(Ok(_)) => {
self.stack.push(Action::GoTo(0, old));
Some(Ok(()))
}
go_to => go_to,
}
}
#[inline]
pub fn commit(self) {}
#[inline]
pub fn cancel(self) -> Result<R> {
for action in self.stack.into_iter().rev() {
match action {
Action::Apply(mut v) => {
if let Some(Err(error)) = self.inner.undo() {
return Err(error);
}
let cursor = self.inner.cursor();
self.inner.commands.truncate(cursor);
self.inner.commands.append(&mut v);
}
Action::Undo => {
if let Some(Err(error)) = self.inner.redo() {
return Err(error);
}
}
Action::Redo => {
if let Some(Err(error)) = self.inner.undo() {
return Err(error);
}
}
Action::GoTo(_, cursor) => {
if let Some(Err(error)) = self.inner.go_to(cursor) {
return Err(error);
}
}
}
}
Ok(())
}
#[inline]
pub fn checkpoint(&mut self) -> Checkpoint<Record<R>, R> {
self.inner.checkpoint()
}
#[inline]
pub fn queue(&mut self) -> Queue<Record<R>, R> {
self.inner.queue()
}
#[inline]
pub fn as_receiver(&self) -> &R {
self.inner.as_receiver()
}
#[inline]
pub fn as_mut_receiver(&mut self) -> &mut R {
self.inner.as_mut_receiver()
}
}
impl<R> AsRef<R> for Checkpoint<'_, Record<R>, R> {
#[inline]
fn as_ref(&self) -> &R {
self.inner.as_ref()
}
}
impl<R> AsMut<R> for Checkpoint<'_, Record<R>, R> {
#[inline]
fn as_mut(&mut self) -> &mut R {
self.inner.as_mut()
}
}
impl<R> Checkpoint<'_, History<R>, R> {
#[inline]
pub fn apply(&mut self, command: impl Command<R> + 'static) -> Result<R>
where
R: 'static,
{
let root = self.inner.root();
let old = self.inner.cursor();
self.inner.__apply(Meta::new(command))?;
self.stack.push(Action::GoTo(root, old));
Ok(())
}
#[inline]
#[must_use]
pub fn undo(&mut self) -> Option<Result<R>> {
match self.inner.undo() {
Some(Ok(_)) => {
self.stack.push(Action::Undo);
Some(Ok(()))
}
undo => undo,
}
}
#[inline]
#[must_use]
pub fn redo(&mut self) -> Option<Result<R>> {
match self.inner.redo() {
Some(Ok(_)) => {
self.stack.push(Action::Redo);
Some(Ok(()))
}
redo => redo,
}
}
#[inline]
#[must_use]
pub fn go_to(&mut self, branch: usize, cursor: usize) -> Option<Result<R>>
where
R: 'static,
{
let root = self.inner.root();
let old = self.inner.cursor();
match self.inner.go_to(branch, cursor) {
Some(Ok(_)) => {
self.stack.push(Action::GoTo(root, old));
Some(Ok(()))
}
go_to => go_to,
}
}
#[inline]
pub fn commit(self) {}
#[inline]
pub fn cancel(self) -> Result<R>
where
R: 'static,
{
for action in self.stack.into_iter().rev() {
match action {
Action::Apply(_) => unreachable!(),
Action::Undo => {
if let Some(Err(error)) = self.inner.redo() {
return Err(error);
}
}
Action::Redo => {
if let Some(Err(error)) = self.inner.undo() {
return Err(error);
}
}
Action::GoTo(branch, cursor) => {
if let Some(Err(error)) = self.inner.go_to(branch, cursor) {
return Err(error);
}
}
}
}
Ok(())
}
#[inline]
pub fn checkpoint(&mut self) -> Checkpoint<History<R>, R> {
self.inner.checkpoint()
}
#[inline]
pub fn queue(&mut self) -> Queue<History<R>, R> {
self.inner.queue()
}
#[inline]
pub fn as_receiver(&self) -> &R {
self.inner.as_receiver()
}
#[inline]
pub fn as_mut_receiver(&mut self) -> &mut R {
self.inner.as_mut_receiver()
}
}
impl<R> AsRef<R> for Checkpoint<'_, History<R>, R> {
#[inline]
fn as_ref(&self) -> &R {
self.inner.as_ref()
}
}
impl<R> AsMut<R> for Checkpoint<'_, History<R>, R> {
#[inline]
fn as_mut(&mut self) -> &mut R {
self.inner.as_mut()
}
}
#[cfg(all(test, not(feature = "display")))]
mod tests {
use crate::{Command, Record};
use std::error::Error;
#[derive(Debug)]
struct Add(char);
impl Command<String> for Add {
fn apply(&mut self, s: &mut String) -> Result<(), Box<dyn Error + Send + Sync>> {
s.push(self.0);
Ok(())
}
fn undo(&mut self, s: &mut String) -> Result<(), Box<dyn Error + Send + Sync>> {
self.0 = s.pop().ok_or("`s` is empty")?;
Ok(())
}
}
#[test]
fn commit() {
let mut record = Record::default();
let mut cp1 = record.checkpoint();
cp1.apply(Add('a')).unwrap();
cp1.apply(Add('b')).unwrap();
cp1.apply(Add('c')).unwrap();
assert_eq!(cp1.as_receiver(), "abc");
let mut cp2 = cp1.checkpoint();
cp2.apply(Add('d')).unwrap();
cp2.apply(Add('e')).unwrap();
cp2.apply(Add('f')).unwrap();
assert_eq!(cp2.as_receiver(), "abcdef");
let mut cp3 = cp2.checkpoint();
cp3.apply(Add('g')).unwrap();
cp3.apply(Add('h')).unwrap();
cp3.apply(Add('i')).unwrap();
assert_eq!(cp3.as_receiver(), "abcdefghi");
cp3.commit();
cp2.commit();
cp1.commit();
assert_eq!(record.as_receiver(), "abcdefghi");
}
#[test]
fn cancel() {
let mut record = Record::default();
let mut cp1 = record.checkpoint();
cp1.apply(Add('a')).unwrap();
cp1.apply(Add('b')).unwrap();
cp1.apply(Add('c')).unwrap();
let mut cp2 = cp1.checkpoint();
cp2.apply(Add('d')).unwrap();
cp2.apply(Add('e')).unwrap();
cp2.apply(Add('f')).unwrap();
let mut cp3 = cp2.checkpoint();
cp3.apply(Add('g')).unwrap();
cp3.apply(Add('h')).unwrap();
cp3.apply(Add('i')).unwrap();
assert_eq!(cp3.as_receiver(), "abcdefghi");
cp3.cancel().unwrap();
assert_eq!(cp2.as_receiver(), "abcdef");
cp2.cancel().unwrap();
assert_eq!(cp1.as_receiver(), "abc");
cp1.cancel().unwrap();
assert_eq!(record.as_receiver(), "");
}
}