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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
//! cola is a Conflict-free Replicated Data Type ([CRDT]) for real-time
//! collaborative editing of plain text documents.
//!
//! CRDTs can be roughly divided into two categories: state-based and
//! operation-based. cola falls in the latter category.
//!
//! The basic idea behind an Operation-based CRDT -- also known as a
//! *Commutative* Replicated Data Type or C*m*RDT -- is to design the core data
//! structure and the operations applied to it in such a way that they
//! *commute*, so that the order in which they're applied at each peer doesn't
//! matter.
//!
//! Commutativity makes the final state of the data structure only a function
//! of its initial state and the *set* of operations applied to it, but *not*
//! of the order in which they were applied. This ensures that once all peers
//! have received all operations from all other peers they're guaranteed to
//! converge to the same final state.
//!
//! In cola, the core data structure which represents the state of the document
//! at each peer is the [`Replica`], and the operations which the peers
//! exchange to communicate their local edits are [`Insertion`]s and
//! [`Deletion`]s.
//!
//! If you're new to this crate, reading the documentations of the
//! [`Replica`] struct and its methods would be a good place to start.
//!
//! For a deeper dive into cola's design and implementation you can check out
//! [this blog post][cola].
//!
//! # Example usage
//!
//! ```rust
//! use std::ops::Range;
//!
//! use cola::{Deletion, Replica, ReplicaId};
//!
//! struct Document {
//! buffer: String,
//! crdt: Replica,
//! }
//!
//! struct Insertion {
//! text: String,
//! crdt: cola::Insertion,
//! }
//!
//! impl Document {
//! fn new<S: Into<String>>(text: S, replica_id: ReplicaId) -> Self {
//! let buffer = text.into();
//! let crdt = Replica::new(replica_id, buffer.len());
//! Document { buffer, crdt }
//! }
//!
//! fn fork(&self, new_replica_id: ReplicaId) -> Self {
//! let crdt = self.crdt.fork(new_replica_id);
//! Document { buffer: self.buffer.clone(), crdt }
//! }
//!
//! fn insert<S: Into<String>>(
//! &mut self,
//! insert_at: usize,
//! text: S,
//! ) -> Insertion {
//! let text = text.into();
//! self.buffer.insert_str(insert_at, &text);
//! let insertion = self.crdt.inserted(insert_at, text.len());
//! Insertion { text, crdt: insertion }
//! }
//!
//! fn delete(&mut self, range: Range<usize>) -> Deletion {
//! self.buffer.replace_range(range.clone(), "");
//! self.crdt.deleted(range)
//! }
//!
//! fn integrate_insertion(&mut self, insertion: Insertion) {
//! if let Some(offset) =
//! self.crdt.integrate_insertion(&insertion.crdt)
//! {
//! self.buffer.insert_str(offset, &insertion.text);
//! }
//! }
//!
//! fn integrate_deletion(&mut self, deletion: Deletion) {
//! let ranges = self.crdt.integrate_deletion(&deletion);
//! for range in ranges.into_iter().rev() {
//! self.buffer.replace_range(range, "");
//! }
//! }
//! }
//!
//! fn main() {
//! let mut peer_1 = Document::new("Hello, world", 1);
//! let mut peer_2 = peer_1.fork(2);
//!
//! let delete_comma = peer_1.delete(5..6);
//! let insert_exclamation = peer_2.insert(12, "!");
//!
//! peer_1.integrate_insertion(insert_exclamation);
//! peer_2.integrate_deletion(delete_comma);
//!
//! assert_eq!(peer_1.buffer, "Hello world!");
//! assert_eq!(peer_2.buffer, "Hello world!");
//! }
//! ```
//!
//! # Feature flags
//!
//! - `encode`: enables the [`encode`](Replica::encode) and
//! [`decode`](Replica::decode) methods on [`Replica`] (disabled by default);
//!
//! - `serde`: enables the [`Serialize`] and [`Deserialize`] impls for
//! [`Insertion`], [`Deletion`] and [`EncodedReplica`] (disabled by default).
//!
//! [CRDT]: https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type
//! [cola]: https://nomad.foo/blog/cola
//! [`Serialize`]: https://docs.rs/serde/latest/serde/trait.Serialize.html
//! [`Deserialize`]: https://docs.rs/serde/latest/serde/trait.Deserialize.html
extern crate alloc;
use Backlog;
pub use ;
pub use ;
use ;
pub use Replica;
use *;
pub use ReplicaId;
use ;
use ;
use *;
pub use Text;
use *;
use ;
use checksum;
pub use ;
/// The version of the protocol cola uses to represent `EncodedReplica`s and
/// `CrdtEdit`s.
///
/// You can think of this as a version number for cola's internal
/// representation of the subset of its data structures that are exchanged
/// between peers.
///
/// If different peers are using versions of cola with the same protocol number
/// they're compatible. If not, decoding `EncodedReplica`s and deserializing
/// `CrdtEdit`s will fail.
///
/// # Protocol stability
///
/// cola is still far away from reaching stability, and until that happens its
/// internal `ProtocolVersion` might change very frequently. After the 1.0
/// release the protocol version will only be allowed to be incremented in
/// major releases (if at all).
pub type ProtocolVersion = u64;
/// The length of a piece of text according to some user-defined metric.
///
/// The meaning of a unit of length is decided by you, the user of this
/// library, depending on the kind of buffer you're using cola with. This
/// allows cola to work with buffers using a variety of encodings (UTF-8,
/// UTF-16, etc.) and indexing metrics (bytes, codepoints, graphemes, etc.).
///
/// While the particular meaning of a unit of length is up to the user, it is
/// important that it is consistent across all peers. For example, if one peer
/// uses bytes as its unit of length, all other peers must also use bytes or
/// the contents of their buffers will diverge.
///
/// # Examples
///
/// In this example all peers use the same metric (codepoints) and everything
/// works as expected:
///
/// ```
/// # use cola::Replica;
/// fn insert_at_codepoint(s: &mut String, offset: usize, insert: &str) {
/// let byte_offset = s.chars().take(offset).map(char::len_utf8).sum();
/// s.insert_str(byte_offset, insert);
/// }
///
/// // Peer 1 uses a String as its buffer and codepoints as its unit of
/// // length.
/// let mut buf1 = String::from("àc");
/// let mut replica1 = Replica::new(1, 2); // "àc" has 2 codepoints.
///
/// let mut buf2 = buf1.clone();
/// let mut replica2 = replica1.fork(2);
///
/// // Peer 1 inserts a 'b' between 'à' and 'c' and sends the edit over to the
/// // other peer.
/// let b = "b";
/// insert_at_codepoint(&mut buf1, 1, b);
/// let insert_b = replica1.inserted(1, 1);
///
/// // Peer 2 receives the edit.
/// let offset = replica2.integrate_insertion(&insert_b).unwrap();
///
/// assert_eq!(offset, 1);
///
/// // Peer 2 also uses codepoints as its unit of length, so it inserts the
/// // 'b' after the 'à' as expected.
/// insert_at_codepoint(&mut buf2, offset, b);
///
/// // If all the peers use the same metric they'll always converge to the
/// // same state.
/// assert_eq!(buf1, "àbc");
/// assert_eq!(buf2, "àbc");
/// ```
///
/// If different peers use different metrics, however, their buffers can
/// diverge or even cause the program to crash, like in the following example:
///
/// ```should_panic
/// # use cola::Replica;
/// # let b = "b";
/// # let mut buf2 = String::from("àc");
/// # let mut replica1 = Replica::new(1, 2);
/// # let mut replica2 = replica1.fork(2);
/// # let insert_b = replica1.inserted(1, 1);
/// // ..same as before.
///
/// assert_eq!(buf2, "àc");
///
/// // Peer 2 receives the edit.
/// let offset = replica2.integrate_insertion(&insert_b).unwrap();
///
/// assert_eq!(offset, 1);
///
/// // Now let's say peer 2 interprets `offset` as a byte offset even though
/// // the insertion of the 'b' was done using codepoint offsets on peer 1.
/// //
/// // In this case the program just panics because a byte offset of 1 is not a
/// // valid insertion point in the string "àc" since it falls in the middle of
/// // the 'à' codepoint, which is 2 bytes long.
/// //
/// // In other cases the program might not panic but instead cause the peers
/// // to silently diverge, which is arguably worse.
///
/// buf2.insert_str(offset, b); // 💥 panics!
/// ```
pub type Length = usize;
/// The protocol version of the current cola release.
///
/// See [`ProtocolVersion`] for more infos.
const PROTOCOL_VERSION: ProtocolVersion = 0;