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

            
4
mod timestamp;
5

            
6
pub use self::timestamp::Timestamp;
7
use crate::Error;
8

            
9
mod implementation {
10
    use arc_bytes::serde::Bytes;
11
    use async_trait::async_trait;
12
    use futures::future::BoxFuture;
13
    use serde::Serialize;
14

            
15
    use crate::{
16
        keyvalue::{Command, KeyCheck, KeyOperation, KeyStatus, Output, Timestamp},
17
        Error,
18
    };
19

            
20
    /// Types for executing get operations.
21
    pub mod get;
22
    /// Types for executing increment/decrement operations.
23
    pub mod increment;
24
    /// Types for handling key namespaces.
25
    pub mod namespaced;
26
    /// Types for executing set operations.
27
    pub mod set;
28

            
29
    use namespaced::Namespaced;
30

            
31
    use super::{IncompatibleTypeError, Numeric, Value};
32

            
33
    /// Key-Value store methods. The Key-Value store is designed to be a
34
    /// high-performance, lightweight storage mechanism.
35
    ///
36
    /// When compared to Collections, the Key-Value store does not offer
37
    /// ACID-compliant transactions. Instead, the Key-Value store is made more
38
    /// efficient by periodically flushing the store to disk rather than during
39
    /// each operation. As such, the Key-Value store is intended to be used as a
40
    /// lightweight caching layer. However, because each of the operations it
41
    /// supports are executed atomically, the Key-Value store can also be
42
    /// utilized for synchronized locking.
43
    ///
44
    /// ## Floating Point Operations
45
    ///
46
    /// When using [`KeyValue::set_numeric_key()`] or any numeric operations, if
47
    /// a [Not a Number (NaN) value][nan] is encountered, [`Error::NotANumber`]
48
    /// will be returned without allowing the operation to succeed.
49
    ///
50
    /// Positive and negative infinity values are allowed, as they do not break
51
    /// comparison operations.
52
    ///
53
    /// [nan]: https://en.wikipedia.org/wiki/NaN
54
    #[async_trait]
55
    pub trait KeyValue: Sized + Send + Sync {
56
        /// Executes a single [`KeyOperation`].
57
        async fn execute_key_operation(&self, op: KeyOperation) -> Result<Output, Error>;
58

            
59
        /// Sets `key` to `value`. This function returns a builder that is also a
60
        /// Future. Awaiting the builder will execute [`Command::Set`] with the options
61
        /// given.
62
95
        fn set_key<'a, S: Into<String>, V: Serialize + Send + Sync>(
63
95
            &'a self,
64
95
            key: S,
65
95
            value: &'a V,
66
95
        ) -> set::Builder<'a, Self, V> {
67
95
            set::Builder::new(
68
95
                self,
69
95
                self.key_namespace().map(Into::into),
70
95
                key.into(),
71
95
                PendingValue::Serializeable(value),
72
95
            )
73
95
        }
74

            
75
        /// Sets `key` to `bytes`. This function returns a builder that is also
76
        /// a Future. Awaiting the builder will execute [`Command::Set`] with
77
        /// the options given.
78
20
        fn set_binary_key<'a, S: Into<String>>(
79
20
            &'a self,
80
20
            key: S,
81
20
            bytes: &'a [u8],
82
20
        ) -> set::Builder<'a, Self, ()> {
83
20
            set::Builder::new(
84
20
                self,
85
20
                self.key_namespace().map(Into::into),
86
20
                key.into(),
87
20
                PendingValue::Bytes(bytes),
88
20
            )
89
20
        }
90

            
91
        /// Sets `key` to `value`. This stores the value as a `Numeric`,
92
        /// enabling atomic math operations to be performed on this key. This
93
        /// function returns a builder that is also a Future. Awaiting the
94
        /// builder will execute [`Command::Set`] with the options given.
95
95
        fn set_numeric_key<S: Into<String>, V: Into<Numeric>>(
96
95
            &self,
97
95
            key: S,
98
95
            value: V,
99
95
        ) -> set::Builder<'_, Self, ()> {
100
95
            set::Builder::new(
101
95
                self,
102
95
                self.key_namespace().map(Into::into),
103
95
                key.into(),
104
95
                PendingValue::Numeric(value.into()),
105
95
            )
106
95
        }
107

            
108
        /// Increments `key` by `value`. The value stored must be a `Numeric`,
109
        /// otherwise an error will be returned. The result of the increment
110
        /// will be the `value`'s type. For example, if the stored value is
111
        /// currently a `u64`, but `value` is a `f64`, the current value will be
112
        /// converted to an `f64`, and the stored value will be an `f64`.
113
50060
        fn increment_key_by<
114
50060
            S: Into<String> + Send + Sync,
115
50060
            V: Into<Numeric> + TryFrom<Numeric, Error = IncompatibleTypeError> + Send + Sync,
116
50060
        >(
117
50060
            &self,
118
50060
            key: S,
119
50060
            value: V,
120
50060
        ) -> increment::Builder<'_, Self, V> {
121
50060
            increment::Builder::new(
122
50060
                self,
123
50060
                self.key_namespace().map(Into::into),
124
50060
                true,
125
50060
                key.into(),
126
50060
                value.into(),
127
50060
            )
128
50060
        }
129

            
130
        /// Decrements `key` by `value`. The value stored must be a `Numeric`,
131
        /// otherwise an error will be returned. The result of the decrement
132
        /// will be the `value`'s type. For example, if the stored value is
133
        /// currently a `u64`, but `value` is a `f64`, the current value will be
134
        /// converted to an `f64`, and the stored value will be an `f64`.
135
50
        fn decrement_key_by<
136
50
            S: Into<String> + Send + Sync,
137
50
            V: Into<Numeric> + TryFrom<Numeric, Error = IncompatibleTypeError> + Send + Sync,
138
50
        >(
139
50
            &self,
140
50
            key: S,
141
50
            value: V,
142
50
        ) -> increment::Builder<'_, Self, V> {
143
50
            increment::Builder::new(
144
50
                self,
145
50
                self.key_namespace().map(Into::into),
146
50
                false,
147
50
                key.into(),
148
50
                value.into(),
149
50
            )
150
50
        }
151

            
152
        /// Gets the value stored at `key`. This function returns a builder that is also a
153
        /// Future. Awaiting the builder will execute [`Command::Get`] with the options
154
        /// given.
155
191
        fn get_key<S: Into<String>>(&'_ self, key: S) -> get::Builder<'_, Self> {
156
191
            get::Builder::new(self, self.key_namespace().map(Into::into), key.into())
157
191
        }
158

            
159
        /// Deletes the value stored at `key`.
160
40
        async fn delete_key<S: Into<String> + Send>(&'_ self, key: S) -> Result<KeyStatus, Error> {
161
40
            match self
162
40
                .execute_key_operation(KeyOperation {
163
40
                    namespace: self.key_namespace().map(ToOwned::to_owned),
164
40
                    key: key.into(),
165
40
                    command: Command::Delete,
166
40
                })
167
16
                .await?
168
            {
169
40
                Output::Status(status) => Ok(status),
170
                Output::Value(_) => unreachable!("invalid output from delete operation"),
171
            }
172
80
        }
173

            
174
        /// The current namespace.
175
50145
        fn key_namespace(&self) -> Option<&'_ str> {
176
50145
            None
177
50145
        }
178

            
179
        /// Access this Key-Value store within a namespace. When using the returned
180
        /// [`Namespaced`] instance, all keys specified will be separated into their
181
        /// own storage designated by `namespace`.
182
21
        fn with_key_namespace(&'_ self, namespace: &str) -> Namespaced<'_, Self> {
183
21
            Namespaced::new(namespace.to_string(), self)
184
21
        }
185
    }
186

            
187
    enum BuilderState<'a, T, V> {
188
        Pending(Option<T>),
189
        Executing(BoxFuture<'a, V>),
190
    }
191

            
192
    #[allow(clippy::redundant_pub_crate)]
193
    pub(crate) enum PendingValue<'a, V> {
194
        Bytes(&'a [u8]),
195
        Serializeable(&'a V),
196
        Numeric(Numeric),
197
    }
198

            
199
    impl<'a, V> PendingValue<'a, V>
200
    where
201
        V: Serialize,
202
    {
203
210
        fn prepare(self) -> Result<Value, Error> {
204
210
            match self {
205
20
                Self::Bytes(bytes) => Ok(Value::Bytes(Bytes::from(bytes))),
206
95
                Self::Serializeable(value) => Ok(Value::Bytes(Bytes::from(pot::to_vec(value)?))),
207
95
                Self::Numeric(numeric) => Ok(Value::Numeric(numeric)),
208
            }
209
210
        }
210
    }
211
}
212

            
213
pub use implementation::*;
214

            
215
/// Checks for existing keys.
216
16
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
217
pub enum KeyCheck {
218
    /// Only allow the operation if an existing key is present.
219
    OnlyIfPresent,
220
    /// Only allow the opeartion if the key isn't present.
221
    OnlyIfVacant,
222
}
223

            
224
40444
#[derive(Clone, Serialize, Deserialize, Debug)]
225
/// An operation performed on a key.
226
pub struct KeyOperation {
227
    /// The namespace for the key.
228
    pub namespace: Option<String>,
229
    /// The key to operate on.
230
    pub key: String,
231
    /// The command to execute.
232
    pub command: Command,
233
}
234

            
235
/// Commands for a key-value store.
236
60566
#[derive(Clone, Serialize, Deserialize, Debug)]
237
pub enum Command {
238
    /// Set a key/value pair.
239
    Set(SetCommand),
240
    /// Get the value from a key.
241
    Get {
242
        /// Remove the key after retrieving the value.
243
        delete: bool,
244
    },
245
    /// Increment a numeric key. Returns an error if the key cannot be
246
    /// deserialized to the same numeric type as `amount`. If `saturating` is
247
    /// true, overflows will be prevented and the value will remain within the
248
    /// numeric bounds.
249
    Increment {
250
        /// The amount to increment by.
251
        amount: Numeric,
252
        /// If true, the result will be constrained to the numerical bounds of
253
        /// the type of `amount`.
254
        saturating: bool,
255
    },
256
    /// Decrement a numeric key. Returns an error if the key cannot be
257
    /// deserialized to the same numeric type as `amount`. If `saturating` is
258
    /// true, overflows will be prevented and the value will remain within the
259
    /// numeric bounds.
260
    Decrement {
261
        /// The amount to increment by.
262
        amount: Numeric,
263
        /// If true, the result will be constrained to the numerical bounds of
264
        /// the type of `amount`.
265
        saturating: bool,
266
    },
267
    /// Delete a key.
268
    Delete,
269
}
270

            
271
/// Set a key/value pair.
272
168
#[derive(Clone, Serialize, Deserialize, Debug)]
273
pub struct SetCommand {
274
    /// The value.
275
    pub value: Value,
276
    /// If set, the key will be set to expire automatically.
277
    pub expiration: Option<Timestamp>,
278
    /// If true and the key already exists, the expiration will not be
279
    /// updated. If false and an expiration is provided, the expiration will
280
    /// be set.
281
    pub keep_existing_expiration: bool,
282
    /// Conditional checks for whether the key is already present or not.
283
    pub check: Option<KeyCheck>,
284
    /// If true and the key already exists, the existing key will be returned if overwritten.
285
    pub return_previous_value: bool,
286
}
287

            
288
/// A value stored in a key.
289
1462176
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
290
pub enum Value {
291
    /// A value stored as a byte array.
292
    Bytes(Bytes),
293
    /// A numeric value.
294
    Numeric(Numeric),
295
}
296

            
297
impl Value {
298
    /// Validates this value to ensure it is safe to store.
299
5590
    pub fn validate(self) -> Result<Self, Error> {
300
5590
        match self {
301
2496
            Self::Numeric(numeric) => numeric.validate().map(Self::Numeric),
302
3094
            Self::Bytes(vec) => Ok(Self::Bytes(vec)),
303
        }
304
5590
    }
305

            
306
    /// Deserializes the bytes contained inside of this value. Returns an error
307
    /// if this value doesn't contain bytes.
308
42
    pub fn deserialize<V: for<'de> Deserialize<'de>>(&self) -> Result<V, Error> {
309
42
        match self {
310
42
            Self::Bytes(bytes) => Ok(pot::from_slice(bytes)?),
311
            Self::Numeric(_) => Err(Error::Database(String::from(
312
                "key contains numeric value, not serialized data",
313
            ))),
314
        }
315
42
    }
316

            
317
    /// Returns this value as an `i64`, allowing for precision to be lost if the type was not an `i64` originally. If saturating is true, the conversion will not allow overflows. Returns None if the value is bytes.
318
    #[must_use]
319
520
    pub fn as_i64_lossy(&self, saturating: bool) -> Option<i64> {
320
520
        match self {
321
            Self::Bytes(_) => None,
322
520
            Self::Numeric(value) => Some(value.as_i64_lossy(saturating)),
323
        }
324
520
    }
325

            
326
    /// Returns this value as an `u64`, allowing for precision to be lost if the type was not an `u64` originally. If saturating is true, the conversion will not allow overflows. Returns None if the value is bytes.
327
    #[must_use]
328
520
    pub fn as_u64_lossy(&self, saturating: bool) -> Option<u64> {
329
520
        match self {
330
            Self::Bytes(_) => None,
331
520
            Self::Numeric(value) => Some(value.as_u64_lossy(saturating)),
332
        }
333
520
    }
334

            
335
    /// Returns this value as an `f64`, allowing for precision to be lost if the type was not an `f64` originally. Returns None if the value is bytes.
336
    #[must_use]
337
260
    pub const fn as_f64_lossy(&self) -> Option<f64> {
338
260
        match self {
339
            Self::Bytes(_) => None,
340
260
            Self::Numeric(value) => Some(value.as_f64_lossy()),
341
        }
342
260
    }
343

            
344
    /// Returns this numeric as an `i64`, allowing for precision to be lost if the type was not an `i64` originally. Returns None if the value is bytes.
345
    #[must_use]
346
520
    pub fn as_i64(&self) -> Option<i64> {
347
520
        match self {
348
            Self::Bytes(_) => None,
349
520
            Self::Numeric(value) => value.as_i64(),
350
        }
351
520
    }
352

            
353
    /// Returns this numeric as an `u64`, allowing for precision to be lost if the type was not an `u64` originally. Returns None if the value is bytes.
354
    #[must_use]
355
624
    pub fn as_u64(&self) -> Option<u64> {
356
624
        match self {
357
            Self::Bytes(_) => None,
358
624
            Self::Numeric(value) => value.as_u64(),
359
        }
360
624
    }
361

            
362
    /// Returns this numeric as an `f64`, allowing for precision to be lost if the type was not an `f64` originally. Returns None if the value is bytes.
363
    #[must_use]
364
780
    pub const fn as_f64(&self) -> Option<f64> {
365
780
        match self {
366
            Self::Bytes(_) => None,
367
780
            Self::Numeric(value) => value.as_f64(),
368
        }
369
780
    }
370
}
371

            
372
/// A numerical value.
373
1778663
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
374
pub enum Numeric {
375
    /// A 64-bit signed integer.
376
    Integer(i64),
377
    /// A 64-bit unsigned integer.
378
    UnsignedInteger(u64),
379
    /// A 64-bit floating point number.
380
    Float(f64),
381
}
382

            
383
impl Numeric {
384
    /// Ensures this value contains a valid value.
385
    ///
386
    /// # Errors
387
    ///
388
    /// [`Error::NotANumber`] is returned if this contains a NaN floating point
389
    /// value.
390
    pub fn validate(self) -> Result<Self, Error> {
391
1305096
        if let Self::Float(float) = self {
392
1430
            if float.is_nan() {
393
260
                return Err(Error::NotANumber);
394
1170
            }
395
1303666
        }
396

            
397
1304836
        Ok(self)
398
1305096
    }
399

            
400
    /// Returns this numeric as an `i64`. If this conversion cannot be done
401
    /// without losing precision or overflowing, None will be returned.
402
    #[must_use]
403
    #[allow(clippy::cast_possible_truncation)]
404
520
    pub fn as_i64(&self) -> Option<i64> {
405
520
        match self {
406
130
            Self::Integer(value) => Some(*value),
407
130
            Self::UnsignedInteger(value) => (*value).try_into().ok(),
408
260
            Self::Float(value) => {
409
260
                if value.fract().abs() > 0. {
410
260
                    None
411
                } else {
412
                    Some(*value as i64)
413
                }
414
            }
415
        }
416
520
    }
417

            
418
    /// Returns this numeric as an `i64`, allowing for precision to be lost if
419
    /// the type was not an `i64` originally. If saturating is true, the
420
    /// conversion will not allow overflows.
421
    #[must_use]
422
    #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
423
1300
    pub fn as_i64_lossy(&self, saturating: bool) -> i64 {
424
1300
        match self {
425
520
            Self::Integer(value) => *value,
426
520
            Self::UnsignedInteger(value) => {
427
520
                if saturating {
428
390
                    (*value).try_into().unwrap_or(i64::MAX)
429
                } else {
430
130
                    *value as i64
431
                }
432
            }
433
260
            Self::Float(value) => *value as i64,
434
        }
435
1300
    }
436

            
437
    /// Returns this numeric as an `u64`. If this conversion cannot be done
438
    /// without losing precision or overflowing, None will be returned.
439
    #[must_use]
440
    #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
441
624
    pub fn as_u64(&self) -> Option<u64> {
442
624
        match self {
443
234
            Self::UnsignedInteger(value) => Some(*value),
444
130
            Self::Integer(value) => (*value).try_into().ok(),
445
260
            Self::Float(value) => {
446
260
                if value.fract() < f64::EPSILON && value.is_sign_positive() {
447
                    Some(*value as u64)
448
                } else {
449
260
                    None
450
                }
451
            }
452
        }
453
624
    }
454

            
455
    /// Returns this numeric as an `u64`, allowing for precision to be lost if
456
    /// the type was not an `i64` originally. If saturating is true, the
457
    /// conversion will not allow overflows.
458
    #[must_use]
459
    #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
460
1301690
    pub fn as_u64_lossy(&self, saturating: bool) -> u64 {
461
1301690
        match self {
462
1301170
            Self::UnsignedInteger(value) => *value,
463
260
            Self::Integer(value) => {
464
260
                if saturating {
465
130
                    (*value).try_into().unwrap_or(0)
466
                } else {
467
130
                    *value as u64
468
                }
469
            }
470
260
            Self::Float(value) => *value as u64,
471
        }
472
1301690
    }
473

            
474
    /// Returns this numeric as an `f64`. If this conversion cannot be done
475
    /// without losing precision, None will be returned.
476
    #[must_use]
477
    #[allow(clippy::cast_precision_loss)]
478
780
    pub const fn as_f64(&self) -> Option<f64> {
479
780
        match self {
480
            Self::UnsignedInteger(value) => {
481
                if *value > 2_u64.pow(f64::MANTISSA_DIGITS) {
482
                    None
483
                } else {
484
                    Some(*value as f64)
485
                }
486
            }
487
520
            Self::Integer(value) => {
488
520
                if *value > 2_i64.pow(f64::MANTISSA_DIGITS)
489
390
                    || *value < -(2_i64.pow(f64::MANTISSA_DIGITS))
490
                {
491
260
                    None
492
                } else {
493
260
                    Some(*value as f64)
494
                }
495
            }
496
260
            Self::Float(value) => Some(*value),
497
        }
498
780
    }
499

            
500
    /// Returns this numeric as an `f64`, allowing for precision to be lost if
501
    /// the type was not an `f64` originally.
502
    #[must_use]
503
    #[allow(clippy::cast_precision_loss)]
504
910
    pub const fn as_f64_lossy(&self) -> f64 {
505
910
        match self {
506
260
            Self::UnsignedInteger(value) => *value as f64,
507
260
            Self::Integer(value) => *value as f64,
508
390
            Self::Float(value) => *value,
509
        }
510
910
    }
511
}
512

            
513
/// A conversion between numeric types wasn't supported.
514
#[derive(thiserror::Error, Debug)]
515
#[error("incompatible numeric type")]
516
pub struct IncompatibleTypeError;
517

            
518
impl From<f64> for Numeric {
519
1430
    fn from(value: f64) -> Self {
520
1430
        Self::Float(value)
521
1430
    }
522
}
523

            
524
impl From<i64> for Numeric {
525
2080
    fn from(value: i64) -> Self {
526
2080
        Self::Integer(value)
527
2080
    }
528
}
529

            
530
impl From<u64> for Numeric {
531
1301820
    fn from(value: u64) -> Self {
532
1301820
        Self::UnsignedInteger(value)
533
1301820
    }
534
}
535

            
536
#[allow(clippy::fallible_impl_from)]
537
impl TryFrom<Numeric> for f64 {
538
    type Error = IncompatibleTypeError;
539
    fn try_from(value: Numeric) -> Result<Self, IncompatibleTypeError> {
540
520
        if let Numeric::Float(value) = value {
541
520
            Ok(value)
542
        } else {
543
            Err(IncompatibleTypeError)
544
        }
545
520
    }
546
}
547

            
548
#[allow(clippy::fallible_impl_from)]
549
impl TryFrom<Numeric> for u64 {
550
    type Error = IncompatibleTypeError;
551
    fn try_from(value: Numeric) -> Result<Self, IncompatibleTypeError> {
552
1301170
        if let Numeric::UnsignedInteger(value) = value {
553
1301170
            Ok(value)
554
        } else {
555
            Err(IncompatibleTypeError)
556
        }
557
1301170
    }
558
}
559

            
560
#[allow(clippy::fallible_impl_from)]
561
impl TryFrom<Numeric> for i64 {
562
    type Error = IncompatibleTypeError;
563
    fn try_from(value: Numeric) -> Result<Self, IncompatibleTypeError> {
564
780
        if let Numeric::Integer(value) = value {
565
780
            Ok(value)
566
        } else {
567
            Err(IncompatibleTypeError)
568
        }
569
780
    }
570
}
571

            
572
/// The result of a [`KeyOperation`].
573
40428
#[derive(Clone, Serialize, Deserialize, Debug)]
574
pub enum Output {
575
    /// A status was returned.
576
    Status(KeyStatus),
577
    /// A value was returned.
578
    Value(Option<Value>),
579
}
580
/// The status of an operation on a Key.
581
219
#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq)]
582
pub enum KeyStatus {
583
    /// A new key was inserted.
584
    Inserted,
585
    /// An existing key was updated with a new value.
586
    Updated,
587
    /// A key was deleted.
588
    Deleted,
589
    /// No changes were made.
590
    NotChanged,
591
}