undo/lib.rs
1//! **An undo-redo library.**
2//!
3//! An implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern),
4//! where all edits are done by creating objects that applies the modifications.
5//! All objects knows how to undo the changes it applies, and by using the provided data
6//! structures it is easy to undo and redo edits made to a target.
7//!
8//! See the [examples](https://github.com/evenorog/undo/tree/master/examples) for more information.
9//!
10//! # Features
11//!
12//! * [`Edit`] provides the base functionality for all edit commands. Multiple edit commands can be merged into a single edit
13//! by implementing the [`merge`](Edit::merge) method on the edit. This allows smaller edits to be used to build
14//! more complex operations, or smaller incremental changes to be merged into larger changes that can be undone and
15//! redone in a single step.
16//! * [`Record`] provides basic stack based undo-redo functionality.
17//! * [`History`] provides full tree based undo-redo functionality.
18//! * Queue and checkpoint functionality is supported for both [`Record`] and [`History`].
19//! * The target can be marked as saved to disk and the user will be notified when it changes.
20//! * The amount of changes being tracked can be configured by the user so only the `N` most recent changes are stored.
21//! * Configurable display formatting using the display structures.
22//!
23//! # Examples
24//!
25//! All examples in the documentation uses the `Add`
26//! command found [here](https://github.com/evenorog/undo/blob/master/src/add.rs).
27//!
28//! # Cargo Feature Flags
29//!
30//! | Name | Default | Enables | Description |
31//! |---------|---------|---------|-----------------------------------------------------------------|
32//! | std | ✓ | alloc | Enables the standard library. |
33//! | alloc | | | Enables the `alloc` crate. |
34//! | colored | | | Enables colored output when visualizing the display structures. |
35//! | serde | | | Enables serialization and deserialization. |
36
37#![doc(html_root_url = "https://docs.rs/undo")]
38#![deny(missing_docs)]
39#![forbid(unsafe_code)]
40#![cfg_attr(not(feature = "std"), no_std)]
41
42#[cfg(feature = "alloc")]
43extern crate alloc;
44
45#[cfg(doctest)]
46#[doc = include_str!("../README.md")]
47pub struct ReadmeDocTest;
48
49#[cfg(feature = "alloc")]
50mod add;
51#[cfg(feature = "alloc")]
52mod entry;
53#[cfg(feature = "alloc")]
54mod format;
55#[cfg(feature = "alloc")]
56pub mod history;
57#[cfg(feature = "alloc")]
58pub mod record;
59#[cfg(feature = "alloc")]
60mod socket;
61
62#[doc(hidden)]
63#[cfg(feature = "alloc")]
64pub use add::Add;
65#[cfg(feature = "alloc")]
66pub use entry::Entry;
67#[cfg(feature = "alloc")]
68pub use history::History;
69#[cfg(feature = "alloc")]
70pub use record::Record;
71#[cfg(feature = "alloc")]
72pub use socket::{Event, Slot};
73
74#[cfg(feature = "alloc")]
75use format::Format;
76#[cfg(feature = "serde")]
77use serde::{Deserialize, Serialize};
78
79/// Base functionality for all edit commands.
80pub trait Edit {
81 /// The target type.
82 type Target;
83 /// The output type.
84 type Output;
85
86 /// Applies the edit command on the target.
87 fn edit(&mut self, target: &mut Self::Target) -> Self::Output;
88
89 /// Restores the state of the target as it was before the edit was applied.
90 fn undo(&mut self, target: &mut Self::Target) -> Self::Output;
91
92 /// Reapplies the edit on the target.
93 ///
94 /// The default implementation uses the [`Edit::edit`] implementation.
95 fn redo(&mut self, target: &mut Self::Target) -> Self::Output {
96 self.edit(target)
97 }
98
99 /// Used for manual merging of edits. See [`Merged`] for more information.
100 fn merge(&mut self, other: Self) -> Merged<Self>
101 where
102 Self: Sized,
103 {
104 Merged::No(other)
105 }
106}
107
108/// Says if the [`Edit`] command have been merged with another command.
109#[derive(Copy, Clone, Debug)]
110pub enum Merged<E> {
111 /// The edits have been merged.
112 ///
113 /// This means that the `other` edit will not be added to the stack.
114 Yes,
115 /// The edits have not been merged.
116 ///
117 /// We need to return the `other` edit so it can be added to the stack.
118 No(E),
119 /// The two edits cancels each other out.
120 ///
121 /// This means that both edits will be removed from the stack.
122 Annul,
123}
124
125/// A position in a history tree.
126#[cfg(feature = "alloc")]
127#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
128#[derive(Copy, Clone, Debug, Eq, PartialEq)]
129pub struct At {
130 /// The root branch.
131 pub root: usize,
132 /// The index of edit.
133 pub index: usize,
134}
135
136#[cfg(feature = "alloc")]
137impl At {
138 const NIL: At = At::new(0, 0);
139
140 /// Creates a new `At` with the provided root and index.
141 pub const fn new(root: usize, index: usize) -> At {
142 At { root, index }
143 }
144
145 const fn no_root(index: usize) -> At {
146 At::new(0, index)
147 }
148}