1
use arc_bytes::serde::Bytes;
2
use serde::{Deserialize, Serialize};
3

            
4
use crate::{
5
    document::{CollectionHeader, DocumentId, Header},
6
    schema::{CollectionName, SerializedCollection},
7
    Error,
8
};
9

            
10
/// A list of operations to execute as a single unit. If any operation fails,
11
/// all changes are aborted. Reads that happen while the transaction is in
12
/// progress will return old data and not block.
13
20081
#[derive(Clone, Serialize, Deserialize, Default, Debug)]
14
#[must_use]
15
pub struct Transaction {
16
    /// The operations in this transaction.
17
    pub operations: Vec<Operation>,
18
}
19

            
20
impl Transaction {
21
    /// Returns a new, empty transaction.
22
156
    pub fn new() -> Self {
23
156
        Self::default()
24
156
    }
25

            
26
    /// Adds an operation to the transaction.
27
202566
    pub fn push(&mut self, operation: Operation) {
28
202566
        self.operations.push(operation);
29
202566
    }
30

            
31
    /// Appends an operation to the transaction and returns self.
32
    pub fn with(mut self, operation: Operation) -> Self {
33
        self.push(operation);
34
        self
35
    }
36
}
37

            
38
impl From<Operation> for Transaction {
39
470314
    fn from(operation: Operation) -> Self {
40
470314
        Self {
41
470314
            operations: vec![operation],
42
470314
        }
43
470314
    }
44
}
45

            
46
impl Transaction {
47
    /// Inserts a new document with `contents` into `collection`.  If `id` is
48
    /// `None` a unique id will be generated. If an id is provided and a
49
    /// document already exists with that id, a conflict error will be returned.
50
227626
    pub fn insert(
51
227626
        collection: CollectionName,
52
227626
        id: Option<DocumentId>,
53
227626
        contents: impl Into<Bytes>,
54
227626
    ) -> Self {
55
227626
        Self::from(Operation::insert(collection, id, contents))
56
227626
    }
57

            
58
    /// Updates a document in `collection`.
59
4928
    pub fn update(collection: CollectionName, header: Header, contents: impl Into<Bytes>) -> Self {
60
4928
        Self::from(Operation::update(collection, header, contents))
61
4928
    }
62

            
63
    /// Overwrites a document in `collection`. If a document with `id` exists,
64
    /// it will be overwritten. If a document with `id` doesn't exist, it will
65
    /// be created.
66
511
    pub fn overwrite(
67
511
        collection: CollectionName,
68
511
        id: DocumentId,
69
511
        contents: impl Into<Bytes>,
70
511
    ) -> Self {
71
511
        Self::from(Operation::overwrite(collection, id, contents))
72
511
    }
73

            
74
    /// Deletes a document from a `collection`.
75
37232
    pub fn delete(collection: CollectionName, header: Header) -> Self {
76
37232
        Self::from(Operation::delete(collection, header))
77
37232
    }
78
}
79

            
80
/// A single operation performed on a `Collection`.
81
27714
#[derive(Clone, Serialize, Deserialize, Debug)]
82
#[must_use]
83
pub struct Operation {
84
    /// The id of the `Collection`.
85
    pub collection: CollectionName,
86

            
87
    /// The command being performed.
88
    pub command: Command,
89
}
90

            
91
impl Operation {
92
    /// Inserts a new document with `contents` into `collection`.  If `id` is
93
    /// `None` a unique id will be generated. If an id is provided and a
94
    /// document already exists with that id, a conflict error will be returned.
95
235407
    pub fn insert(
96
235407
        collection: CollectionName,
97
235407
        id: Option<DocumentId>,
98
235407
        contents: impl Into<Bytes>,
99
235407
    ) -> Self {
100
235407
        Self {
101
235407
            collection,
102
235407
            command: Command::Insert {
103
235407
                id,
104
235407
                contents: contents.into(),
105
235407
            },
106
235407
        }
107
235407
    }
108

            
109
    /// Inserts a new document with the serialized representation of `contents`
110
    /// into `collection`.  If `id` is `None` a unique id will be generated. If
111
    /// an id is provided and a document already exists with that id, a conflict
112
    /// error will be returned.
113
7776
    pub fn insert_serialized<C: SerializedCollection>(
114
7776
        id: Option<C::PrimaryKey>,
115
7776
        contents: &C::Contents,
116
7776
    ) -> Result<Self, Error> {
117
7776
        let id = id.map(DocumentId::new).transpose()?;
118
7776
        let contents = C::serialize(contents)?;
119
7776
        Ok(Self::insert(C::collection_name(), id, contents))
120
7776
    }
121

            
122
    /// Updates a document in `collection`.
123
4928
    pub fn update(collection: CollectionName, header: Header, contents: impl Into<Bytes>) -> Self {
124
4928
        Self {
125
4928
            collection,
126
4928
            command: Command::Update {
127
4928
                header,
128
4928
                contents: contents.into(),
129
4928
            },
130
4928
        }
131
4928
    }
132

            
133
    /// Updates a document with the serialized representation of `contents` in
134
    /// `collection`.
135
    pub fn update_serialized<C: SerializedCollection>(
136
        header: CollectionHeader<C::PrimaryKey>,
137
        contents: &C::Contents,
138
    ) -> Result<Self, Error> {
139
        let contents = C::serialize(contents)?;
140
        Ok(Self::update(
141
            C::collection_name(),
142
            Header::try_from(header)?,
143
            contents,
144
        ))
145
    }
146

            
147
    /// Overwrites a document in `collection`. If a document with `id` exists,
148
    /// it will be overwritten. If a document with `id` doesn't exist, it will
149
    /// be created.
150
511
    pub fn overwrite(
151
511
        collection: CollectionName,
152
511
        id: DocumentId,
153
511
        contents: impl Into<Bytes>,
154
511
    ) -> Self {
155
511
        Self {
156
511
            collection,
157
511
            command: Command::Overwrite {
158
511
                id,
159
511
                contents: contents.into(),
160
511
            },
161
511
        }
162
511
    }
163

            
164
    /// Overwrites a document with the serialized representation of `contents`
165
    /// in `collection`. If a document with `id` exists, it will be overwritten.
166
    /// If a document with `id` doesn't exist, it will be created.
167
    pub fn overwrite_serialized<C: SerializedCollection>(
168
        id: C::PrimaryKey,
169
        contents: &C::Contents,
170
    ) -> Result<Self, Error> {
171
        let contents = C::serialize(contents)?;
172
        Ok(Self::overwrite(
173
            C::collection_name(),
174
            DocumentId::new(id)?,
175
            contents,
176
        ))
177
    }
178

            
179
    /// Deletes a document from a `collection`.
180
37492
    pub const fn delete(collection: CollectionName, header: Header) -> Self {
181
37492
        Self {
182
37492
            collection,
183
37492
            command: Command::Delete { header },
184
37492
        }
185
37492
    }
186
}
187

            
188
/// A command to execute within a `Collection`.
189
41571
#[derive(Clone, Serialize, Deserialize, Debug)]
190
pub enum Command {
191
    /// Inserts a new document containing `contents`.
192
    Insert {
193
        /// An optional id for the document. If this is `None`, a unique id will
194
        /// be generated. If this is `Some()` and a document already exists with
195
        /// that id, a conflict error will be returned.
196
        id: Option<DocumentId>,
197
        /// The initial contents of the document.
198
        contents: Bytes,
199
    },
200

            
201
    /// Update an existing `Document` identified by `header`. `header.revision` must match
202
    /// the currently stored revision on the `Document`. If it does not, the
203
    /// command fill fail with a `DocumentConflict` error.
204
    Update {
205
        /// The header of the `Document`. The revision must match the current
206
        /// document.
207
        header: Header,
208

            
209
        /// The new contents to store within the `Document`.
210
        contents: Bytes,
211
    },
212

            
213
    /// Overwrite an existing `Document` identified by `id`. The revision will
214
    /// not be checked before the document is updated. If the document does not
215
    /// exist, it will be created.
216
    Overwrite {
217
        /// The id of the document to overwrite.
218
        id: DocumentId,
219

            
220
        /// The new contents to store within the `Document`.
221
        contents: Bytes,
222
    },
223

            
224
    /// Delete an existing `Document` identified by `id`. `revision` must match
225
    /// the currently stored revision on the `Document`. If it does not, the
226
    /// command fill fail with a `DocumentConflict` error.
227
    Delete {
228
        /// The current header of the `Document`.
229
        header: Header,
230
    },
231
}
232

            
233
/// Information about the result of each `Operation` in a transaction.
234
40023
#[derive(Clone, Debug, Serialize, Deserialize)]
235
pub enum OperationResult {
236
    /// An operation succeeded but had no information to output.
237
    Success,
238

            
239
    /// A `Document` was updated.
240
    DocumentUpdated {
241
        /// The id of the `Collection` of the updated `Document`.
242
        collection: CollectionName,
243

            
244
        /// The header of the updated `Document`.
245
        header: Header,
246
    },
247

            
248
    /// A `Document` was deleted.
249
    DocumentDeleted {
250
        /// The id of the `Collection` of the deleted `Document`.
251
        collection: CollectionName,
252

            
253
        /// The id of the deleted `Document`.
254
        id: DocumentId,
255
    },
256
}
257

            
258
/// Details about an executed transaction.
259
11444
#[derive(Clone, Debug, Serialize, Deserialize)]
260
pub struct Executed {
261
    /// The id of the transaction.
262
    pub id: u64,
263

            
264
    /// A list of containing ids of `Documents` changed.
265
    pub changes: Changes,
266
}
267

            
268
/// A list of changes.
269
557388
#[derive(Clone, Debug, Serialize, Deserialize)]
270
pub enum Changes {
271
    /// A list of changed documents.
272
    Documents(Vec<ChangedDocument>),
273
    /// A list of changed keys.
274
    Keys(Vec<ChangedKey>),
275
}
276

            
277
impl Changes {
278
    /// Returns the list of documents changed in this transaction, or None if
279
    /// the transaction was not a document transaction.
280
    #[must_use]
281
    pub fn documents(&self) -> Option<&[ChangedDocument]> {
282
39468
        if let Self::Documents(docs) = self {
283
39468
            Some(docs)
284
        } else {
285
            None
286
        }
287
39468
    }
288

            
289
    /// Returns the list of keys changed in this transaction, or None if the
290
    /// transaction was not a `KeyValue` transaction.
291
    #[must_use]
292
    pub fn keys(&self) -> Option<&[ChangedKey]> {
293
2730
        if let Self::Keys(keys) = self {
294
2730
            Some(keys)
295
        } else {
296
            None
297
        }
298
2730
    }
299
}
300

            
301
/// A record of a changed document.
302
1453285
#[derive(Debug, Clone, Serialize, Deserialize)]
303
pub struct ChangedDocument {
304
    /// The id of the `Collection` of the changed `Document`.
305
    pub collection: CollectionName,
306

            
307
    /// The id of the changed `Document`.
308
    pub id: DocumentId,
309

            
310
    /// If the `Document` has been deleted, this will be `true`.
311
    pub deleted: bool,
312
}
313

            
314
/// A record of a changed `KeyValue` entry.
315
154476
#[derive(Clone, Debug, Serialize, Deserialize)]
316
pub struct ChangedKey {
317
    /// The namespace of the key.
318
    pub namespace: Option<String>,
319

            
320
    /// The key that was changed.
321
    pub key: String,
322

            
323
    /// True if the key was deleted.
324
    pub deleted: bool,
325
}