1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//! An undo/redo library with static dispatch and manual command merging.
//! It uses the [Command Pattern](https://en.wikipedia.org/wiki/Command_pattern)
//! where the user modifies a receiver by applying `Command`s on it.
//!
//! The library has currently two data structures that can be used to modify the receiver:
//!
//! * A simple `Stack` that pushes and pops commands to modify the receiver.
//! * A `Record` that can roll the state of the receiver forwards and backwards.

#![forbid(unstable_features, bad_style)]
#![deny(missing_debug_implementations,
        unused_import_braces,
        unused_qualifications)]

extern crate fnv;

pub mod record;
mod stack;

use std::fmt::{self, Debug, Display, Formatter};

pub use record::Record;
pub use stack::Stack;

/// Base functionality for all commands.
pub trait Command<R> {
    /// The error type.
    type Err;

    /// Executes the desired command and returns `Ok` if everything went fine, and `Err` if
    /// something went wrong.
    fn redo(&mut self, receiver: &mut R) -> Result<(), Self::Err>;

    /// Restores the state as it was before [`redo`] was called and returns `Ok` if everything
    /// went fine, and `Err` if something went wrong.
    ///
    /// [`redo`]: trait.Command.html#tymethod.redo
    fn undo(&mut self, receiver: &mut R) -> Result<(), Self::Err>;

    /// Used for manual merging of two `Command`s.
    ///
    /// Returns `Some(Ok)` if the merging was successful, `Some(Err)` if something went wrong when
    /// trying to merge, and `None` if it did not try to merge.
    /// This method is called with `self` being the top command and `cmd` being the
    /// new command. If `None` is returned from this method, `cmd` will be pushed
    /// as normal. However, if the return value is `Some(x)` it will not push the command on to
    /// the stack since either it was merged or an error has occurred, and then the stack returns
    /// the `x` value.
    ///
    /// [`push`]: struct.Stack.html#method.push
    #[inline]
    fn merge(&mut self, _: &Self) -> Option<Result<(), Self::Err>> {
        None
    }
}

/// Custom error kind that holds the error and the command that caused the error.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Error<R, C: Command<R>>(pub C, pub C::Err);

impl<R, C: Command<R>> Display for Error<R, C>
where
    C::Err: Display
{
    #[inline]
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", self.1)
    }
}

impl<R, C: Command<R>> std::error::Error for Error<R, C>
where
    R: Debug,
    C: Debug,
    C::Err: std::error::Error,
{
    #[inline]
    fn description(&self) -> &str {
        self.1.description()
    }

    #[inline]
    fn cause(&self) -> Option<&std::error::Error> {
        self.1.cause()
    }
}