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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! **An undo-redo library.**
//!
//! It is an implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern),
//! where all modifications are done by creating objects that applies the modifications.
//! All objects knows how to undo the changes it applies, and by using the provided data
//! structures it is easy to apply, undo, and redo changes made to a target.
//!
//! # Features
//!
//! * [`Action`] provides the base functionality for all actions. Multiple [`Action`]s can be merged into a single action
//!   by implementing the [`merge`](Action::merge) method on the action. This allows smaller actions to be used to build
//!   more complex operations, or smaller incremental changes to be merged into larger changes that can be undone and
//!   redone in a single step.
//! * [`Record`] provides basic stack based undo-redo functionality.
//! * [`History`] provides full tree based undo-redo functionality.
//! * Queue and checkpoint functionality is supported for both [`Record`] and [`History`].
//! * The target can be marked as saved to disk and the user will be notified when it changes.
//! * The amount of changes being tracked can be configured by the user so only the `N` most recent changes are stored.
//! * Configurable display formatting using the display structures.
//!
//! # Cargo Feature Flags
//!
//! | Name    | Default | Description                                                     |
//! |---------|---------|-----------------------------------------------------------------|
//! | colored |         | Enables colored output when visualizing the display structures. |
//! | serde   |         | Enables serialization and deserialization.                      |

#![doc(html_root_url = "https://docs.rs/undo")]
#![deny(missing_docs)]
#![forbid(unsafe_code)]

#[cfg(doctest)]
#[doc = include_str!("../README.md")]
pub struct ReadmeDocTest;

mod any;
mod entry;
mod format;
mod from_fn;
pub mod history;
mod join;
pub mod record;
mod socket;

use entry::Entry;
use format::Format;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

pub use any::Any;
pub use from_fn::{FromFn, TryFromFn};
pub use history::History;
pub use join::{Join, TryJoin};
pub use record::Record;
pub use socket::{Nop, Signal, Slot};

/// Base functionality for all actions.
pub trait Action {
    /// The target type.
    type Target;
    /// The output type.
    type Output;

    /// Applies the action on the target.
    fn apply(&mut self, target: &mut Self::Target) -> Self::Output;

    /// Restores the state of the target as it was before the action was applied.
    fn undo(&mut self, target: &mut Self::Target) -> Self::Output;

    /// Reapplies the action on the target.
    ///
    /// The default implementation uses the [`Action::apply`] implementation.
    fn redo(&mut self, target: &mut Self::Target) -> Self::Output {
        self.apply(target)
    }

    /// Used for manual merging of actions. See [`Merged`] for more information.
    fn merge(&mut self, other: Self) -> Merged<Self>
    where
        Self: Sized,
    {
        Merged::No(other)
    }
}

/// Says if the action have been merged with another action.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum Merged<A> {
    /// The actions have been merged.
    ///
    /// This means that the `other` action will not be added to the stack.
    Yes,
    /// The actions have not been merged.
    ///
    /// We need to return the `other` action so it can be added to the stack.
    No(A),
    /// The two actions cancels each other out.
    ///
    /// This means that both action will be removed from the stack.
    Annul,
}

/// A position in a history tree.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
struct At {
    branch: usize,
    current: usize,
}

impl At {
    const fn new(branch: usize, current: usize) -> At {
        At { branch, current }
    }

    const fn root(current: usize) -> At {
        At::new(0, current)
    }
}