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

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

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

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

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

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

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

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

            
169
        /// The current namespace.
170
30100
        fn key_namespace(&self) -> Option<&'_ str> {
171
30100
            None
172
30100
        }
173

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

            
182
    /// Key-Value store methods. The Key-Value store is designed to be a
183
    /// high-performance, lightweight storage mechanism.
184
    ///
185
    /// When compared to Collections, the Key-Value store does not offer
186
    /// ACID-compliant transactions. Instead, the Key-Value store is made more
187
    /// efficient by periodically flushing the store to disk rather than during
188
    /// each operation. As such, the Key-Value store is intended to be used as a
189
    /// lightweight caching layer. However, because each of the operations it
190
    /// supports are executed atomically, the Key-Value store can also be
191
    /// utilized for synchronized locking.
192
    ///
193
    /// ## Floating Point Operations
194
    ///
195
    /// When using [`KeyValue::set_numeric_key()`] or any numeric operations, if
196
    /// a [Not a Number (NaN) value][nan] is encountered, [`Error::NotANumber`]
197
    /// will be returned without allowing the operation to succeed.
198
    ///
199
    /// Positive and negative infinity values are allowed, as they do not break
200
    /// comparison operations.
201
    ///
202
    /// [nan]: https://en.wikipedia.org/wiki/NaN
203
    #[async_trait]
204
    pub trait AsyncKeyValue: Sized + Send + Sync {
205
        /// Executes a single [`KeyOperation`].
206
        async fn execute_key_operation(&self, op: KeyOperation) -> Result<Output, Error>;
207

            
208
        /// Sets `key` to `value`. This function returns a builder that is also a
209
        /// Future. Awaiting the builder will execute [`Command::Set`] with the options
210
        /// given.
211
94
        fn set_key<'a, S: Into<String>, V: Serialize + Send + Sync>(
212
94
            &'a self,
213
94
            key: S,
214
94
            value: &'a V,
215
94
        ) -> set::AsyncBuilder<'a, Self, V> {
216
94
            set::AsyncBuilder::new(
217
94
                self,
218
94
                self.key_namespace().map(Into::into),
219
94
                key.into(),
220
94
                PendingValue::Serializeable(value),
221
94
            )
222
94
        }
223

            
224
        /// Sets `key` to `bytes`. This function returns a builder that is also
225
        /// a Future. Awaiting the builder will execute [`Command::Set`] with
226
        /// the options given.
227
20
        fn set_binary_key<'a, S: Into<String>>(
228
20
            &'a self,
229
20
            key: S,
230
20
            bytes: &'a [u8],
231
20
        ) -> set::AsyncBuilder<'a, Self, ()> {
232
20
            set::AsyncBuilder::new(
233
20
                self,
234
20
                self.key_namespace().map(Into::into),
235
20
                key.into(),
236
20
                PendingValue::Bytes(bytes),
237
20
            )
238
20
        }
239

            
240
        /// Sets `key` to `value`. This stores the value as a `Numeric`,
241
        /// enabling atomic math operations to be performed on this key. This
242
        /// function returns a builder that is also a Future. Awaiting the
243
        /// builder will execute [`Command::Set`] with the options given.
244
92
        fn set_numeric_key<S: Into<String>, V: Into<Numeric>>(
245
92
            &self,
246
92
            key: S,
247
92
            value: V,
248
92
        ) -> set::AsyncBuilder<'_, Self, ()> {
249
92
            set::AsyncBuilder::new(
250
92
                self,
251
92
                self.key_namespace().map(Into::into),
252
92
                key.into(),
253
92
                PendingValue::Numeric(value.into()),
254
92
            )
255
92
        }
256

            
257
        /// Increments `key` by `value`. The value stored must be a `Numeric`,
258
        /// otherwise an error will be returned. The result of the increment
259
        /// will be the `value`'s type. For example, if the stored value is
260
        /// currently a `u64`, but `value` is a `f64`, the current value will be
261
        /// converted to an `f64`, and the stored value will be an `f64`.
262
50064
        fn increment_key_by<
263
50064
            S: Into<String> + Send + Sync,
264
50064
            V: Into<Numeric> + TryFrom<Numeric, Error = IncompatibleTypeError> + Send + Sync,
265
50064
        >(
266
50064
            &self,
267
50064
            key: S,
268
50064
            value: V,
269
50064
        ) -> increment::AsyncBuilder<'_, Self, V> {
270
50064
            increment::AsyncBuilder::new(
271
50064
                self,
272
50064
                self.key_namespace().map(Into::into),
273
50064
                true,
274
50064
                key.into(),
275
50064
                value.into(),
276
50064
            )
277
50064
        }
278

            
279
        /// Decrements `key` by `value`. The value stored must be a `Numeric`,
280
        /// otherwise an error will be returned. The result of the decrement
281
        /// will be the `value`'s type. For example, if the stored value is
282
        /// currently a `u64`, but `value` is a `f64`, the current value will be
283
        /// converted to an `f64`, and the stored value will be an `f64`.
284
50
        fn decrement_key_by<
285
50
            S: Into<String> + Send + Sync,
286
50
            V: Into<Numeric> + TryFrom<Numeric, Error = IncompatibleTypeError> + Send + Sync,
287
50
        >(
288
50
            &self,
289
50
            key: S,
290
50
            value: V,
291
50
        ) -> increment::AsyncBuilder<'_, Self, V> {
292
50
            increment::AsyncBuilder::new(
293
50
                self,
294
50
                self.key_namespace().map(Into::into),
295
50
                false,
296
50
                key.into(),
297
50
                value.into(),
298
50
            )
299
50
        }
300

            
301
        /// Gets the value stored at `key`. This function returns a builder that is also a
302
        /// Future. Awaiting the builder will execute [`Command::Get`] with the options
303
        /// given.
304
186
        fn get_key<S: Into<String>>(&'_ self, key: S) -> get::AsyncBuilder<'_, Self> {
305
186
            get::AsyncBuilder::new(self, self.key_namespace().map(Into::into), key.into())
306
186
        }
307

            
308
        /// Deletes the value stored at `key`.
309
40
        async fn delete_key<S: Into<String> + Send>(&'_ self, key: S) -> Result<KeyStatus, Error> {
310
40
            match self
311
40
                .execute_key_operation(KeyOperation {
312
40
                    namespace: self.key_namespace().map(ToOwned::to_owned),
313
40
                    key: key.into(),
314
40
                    command: Command::Delete,
315
40
                })
316
40
                .await?
317
            {
318
40
                Output::Status(status) => Ok(status),
319
                Output::Value(_) => unreachable!("invalid output from delete operation"),
320
            }
321
80
        }
322

            
323
        /// The current namespace.
324
50140
        fn key_namespace(&self) -> Option<&'_ str> {
325
50140
            None
326
50140
        }
327

            
328
        /// Access this Key-Value store within a namespace. When using the returned
329
        /// [`Namespaced`] instance, all keys specified will be separated into their
330
        /// own storage designated by `namespace`.
331
21
        fn with_key_namespace(&'_ self, namespace: &str) -> Namespaced<'_, Self> {
332
21
            Namespaced::new(namespace.to_string(), self)
333
21
        }
334
    }
335

            
336
    enum BuilderState<'a, T, V> {
337
        Pending(Option<T>),
338
        Executing(BoxFuture<'a, V>),
339
    }
340

            
341
    #[allow(clippy::redundant_pub_crate)]
342
    pub(crate) enum PendingValue<'a, V> {
343
        Bytes(&'a [u8]),
344
        Serializeable(&'a V),
345
        Numeric(Numeric),
346
    }
347

            
348
    impl<'a, V> PendingValue<'a, V>
349
    where
350
        V: Serialize,
351
    {
352
330
        fn prepare(self) -> Result<Value, Error> {
353
330
            match self {
354
20
                Self::Bytes(bytes) => Ok(Value::Bytes(Bytes::from(bytes))),
355
157
                Self::Serializeable(value) => Ok(Value::Bytes(Bytes::from(pot::to_vec(value)?))),
356
153
                Self::Numeric(numeric) => Ok(Value::Numeric(numeric)),
357
            }
358
330
        }
359
    }
360
}
361

            
362
pub use implementation::*;
363

            
364
/// Checks for existing keys.
365
240
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
366
pub enum KeyCheck {
367
    /// Only allow the operation if an existing key is present.
368
    OnlyIfPresent,
369
    /// Only allow the opeartion if the key isn't present.
370
    OnlyIfVacant,
371
}
372

            
373
757893
#[derive(Clone, Serialize, Deserialize, Debug)]
374
/// An operation performed on a key.
375
pub struct KeyOperation {
376
    /// The namespace for the key.
377
    pub namespace: Option<String>,
378
    /// The key to operate on.
379
    pub key: String,
380
    /// The command to execute.
381
    pub command: Command,
382
}
383

            
384
/// Commands for a key-value store.
385
757029
#[derive(Clone, Serialize, Deserialize, Debug)]
386
pub enum Command {
387
    /// Set a key/value pair.
388
    Set(SetCommand),
389
    /// Get the value from a key.
390
    Get {
391
        /// Remove the key after retrieving the value.
392
        delete: bool,
393
    },
394
    /// Increment a numeric key. Returns an error if the key cannot be
395
    /// deserialized to the same numeric type as `amount`. If `saturating` is
396
    /// true, overflows will be prevented and the value will remain within the
397
    /// numeric bounds.
398
    Increment {
399
        /// The amount to increment by.
400
        amount: Numeric,
401
        /// If true, the result will be constrained to the numerical bounds of
402
        /// the type of `amount`.
403
        saturating: bool,
404
    },
405
    /// Decrement a numeric key. Returns an error if the key cannot be
406
    /// deserialized to the same numeric type as `amount`. If `saturating` is
407
    /// true, overflows will be prevented and the value will remain within the
408
    /// numeric bounds.
409
    Decrement {
410
        /// The amount to increment by.
411
        amount: Numeric,
412
        /// If true, the result will be constrained to the numerical bounds of
413
        /// the type of `amount`.
414
        saturating: bool,
415
    },
416
    /// Delete a key.
417
    Delete,
418
}
419

            
420
/// Set a key/value pair.
421
3394
#[derive(Clone, Serialize, Deserialize, Debug)]
422
pub struct SetCommand {
423
    /// The value.
424
    pub value: Value,
425
    /// If set, the key will be set to expire automatically.
426
    pub expiration: Option<Timestamp>,
427
    /// If true and the key already exists, the expiration will not be
428
    /// updated. If false and an expiration is provided, the expiration will
429
    /// be set.
430
    pub keep_existing_expiration: bool,
431
    /// Conditional checks for whether the key is already present or not.
432
    pub check: Option<KeyCheck>,
433
    /// If true and the key already exists, the existing key will be returned if overwritten.
434
    pub return_previous_value: bool,
435
}
436

            
437
/// A value stored in a key.
438
2483527
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
439
pub enum Value {
440
    /// A value stored as a byte array.
441
    Bytes(Bytes),
442
    /// A numeric value.
443
    Numeric(Numeric),
444
}
445

            
446
impl Value {
447
    /// Validates this value to ensure it is safe to store.
448
10385
    pub fn validate(self) -> Result<Self, Error> {
449
10385
        match self {
450
4774
            Self::Numeric(numeric) => numeric.validate().map(Self::Numeric),
451
5611
            Self::Bytes(vec) => Ok(Self::Bytes(vec)),
452
        }
453
10385
    }
454

            
455
    /// Deserializes the bytes contained inside of this value. Returns an error
456
    /// if this value doesn't contain bytes.
457
80
    pub fn deserialize<V: for<'de> Deserialize<'de>>(&self) -> Result<V, Error> {
458
80
        match self {
459
80
            Self::Bytes(bytes) => Ok(pot::from_slice(bytes)?),
460
            Self::Numeric(_) => Err(Error::Database(String::from(
461
                "key contains numeric value, not serialized data",
462
            ))),
463
        }
464
80
    }
465

            
466
    /// 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.
467
    #[must_use]
468
992
    pub fn as_i64_lossy(&self, saturating: bool) -> Option<i64> {
469
992
        match self {
470
            Self::Bytes(_) => None,
471
992
            Self::Numeric(value) => Some(value.as_i64_lossy(saturating)),
472
        }
473
992
    }
474

            
475
    /// 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.
476
    #[must_use]
477
992
    pub fn as_u64_lossy(&self, saturating: bool) -> Option<u64> {
478
992
        match self {
479
            Self::Bytes(_) => None,
480
992
            Self::Numeric(value) => Some(value.as_u64_lossy(saturating)),
481
        }
482
992
    }
483

            
484
    /// 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.
485
    #[must_use]
486
496
    pub const fn as_f64_lossy(&self) -> Option<f64> {
487
496
        match self {
488
            Self::Bytes(_) => None,
489
496
            Self::Numeric(value) => Some(value.as_f64_lossy()),
490
        }
491
496
    }
492

            
493
    /// 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.
494
    #[must_use]
495
992
    pub fn as_i64(&self) -> Option<i64> {
496
992
        match self {
497
            Self::Bytes(_) => None,
498
992
            Self::Numeric(value) => value.as_i64(),
499
        }
500
992
    }
501

            
502
    /// 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.
503
    #[must_use]
504
1488
    pub fn as_u64(&self) -> Option<u64> {
505
1488
        match self {
506
            Self::Bytes(_) => None,
507
1488
            Self::Numeric(value) => value.as_u64(),
508
        }
509
1488
    }
510

            
511
    /// 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.
512
    #[must_use]
513
1488
    pub const fn as_f64(&self) -> Option<f64> {
514
1488
        match self {
515
            Self::Bytes(_) => None,
516
1488
            Self::Numeric(value) => value.as_f64(),
517
        }
518
1488
    }
519
}
520

            
521
/// A numerical value.
522
3258262
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
523
pub enum Numeric {
524
    /// A 64-bit signed integer.
525
    Integer(i64),
526
    /// A 64-bit unsigned integer.
527
    UnsignedInteger(u64),
528
    /// A 64-bit floating point number.
529
    Float(f64),
530
}
531

            
532
impl Numeric {
533
    /// Ensures this value contains a valid value.
534
    ///
535
    /// # Errors
536
    ///
537
    /// [`Error::NotANumber`] is returned if this contains a NaN floating point
538
    /// value.
539
    pub fn validate(self) -> Result<Self, Error> {
540
2489765
        if let Self::Float(float) = self {
541
2728
            if float.is_nan() {
542
496
                return Err(Error::NotANumber);
543
2232
            }
544
2487037
        }
545

            
546
2489269
        Ok(self)
547
2489765
    }
548

            
549
    /// Returns this numeric as an `i64`. If this conversion cannot be done
550
    /// without losing precision or overflowing, None will be returned.
551
    #[must_use]
552
    #[allow(clippy::cast_possible_truncation)]
553
992
    pub fn as_i64(&self) -> Option<i64> {
554
992
        match self {
555
248
            Self::Integer(value) => Some(*value),
556
248
            Self::UnsignedInteger(value) => (*value).try_into().ok(),
557
496
            Self::Float(value) => {
558
496
                if value.fract().abs() > 0. {
559
496
                    None
560
                } else {
561
                    Some(*value as i64)
562
                }
563
            }
564
        }
565
992
    }
566

            
567
    /// Returns this numeric as an `i64`, allowing for precision to be lost if
568
    /// the type was not an `i64` originally. If saturating is true, the
569
    /// conversion will not allow overflows.
570
    #[must_use]
571
    #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
572
2480
    pub fn as_i64_lossy(&self, saturating: bool) -> i64 {
573
2480
        match self {
574
992
            Self::Integer(value) => *value,
575
992
            Self::UnsignedInteger(value) => {
576
992
                if saturating {
577
744
                    (*value).try_into().unwrap_or(i64::MAX)
578
                } else {
579
248
                    *value as i64
580
                }
581
            }
582
496
            Self::Float(value) => *value as i64,
583
        }
584
2480
    }
585

            
586
    /// Returns this numeric as an `u64`. If this conversion cannot be done
587
    /// without losing precision or overflowing, None will be returned.
588
    #[must_use]
589
    #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
590
1488
    pub fn as_u64(&self) -> Option<u64> {
591
1488
        match self {
592
744
            Self::UnsignedInteger(value) => Some(*value),
593
248
            Self::Integer(value) => (*value).try_into().ok(),
594
496
            Self::Float(value) => {
595
496
                if value.fract() < f64::EPSILON && value.is_sign_positive() {
596
                    Some(*value as u64)
597
                } else {
598
496
                    None
599
                }
600
            }
601
        }
602
1488
    }
603

            
604
    /// Returns this numeric as an `u64`, allowing for precision to be lost if
605
    /// the type was not an `i64` originally. If saturating is true, the
606
    /// conversion will not allow overflows.
607
    #[must_use]
608
    #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
609
2483255
    pub fn as_u64_lossy(&self, saturating: bool) -> u64 {
610
2483255
        match self {
611
2482263
            Self::UnsignedInteger(value) => *value,
612
496
            Self::Integer(value) => {
613
496
                if saturating {
614
248
                    (*value).try_into().unwrap_or(0)
615
                } else {
616
248
                    *value as u64
617
                }
618
            }
619
496
            Self::Float(value) => *value as u64,
620
        }
621
2483255
    }
622

            
623
    /// Returns this numeric as an `f64`. If this conversion cannot be done
624
    /// without losing precision, None will be returned.
625
    #[must_use]
626
    #[allow(clippy::cast_precision_loss)]
627
1488
    pub const fn as_f64(&self) -> Option<f64> {
628
1488
        match self {
629
            Self::UnsignedInteger(value) => {
630
                if *value > 2_u64.pow(f64::MANTISSA_DIGITS) {
631
                    None
632
                } else {
633
                    Some(*value as f64)
634
                }
635
            }
636
992
            Self::Integer(value) => {
637
992
                if *value > 2_i64.pow(f64::MANTISSA_DIGITS)
638
744
                    || *value < -(2_i64.pow(f64::MANTISSA_DIGITS))
639
                {
640
496
                    None
641
                } else {
642
496
                    Some(*value as f64)
643
                }
644
            }
645
496
            Self::Float(value) => Some(*value),
646
        }
647
1488
    }
648

            
649
    /// Returns this numeric as an `f64`, allowing for precision to be lost if
650
    /// the type was not an `f64` originally.
651
    #[must_use]
652
    #[allow(clippy::cast_precision_loss)]
653
1736
    pub const fn as_f64_lossy(&self) -> f64 {
654
1736
        match self {
655
496
            Self::UnsignedInteger(value) => *value as f64,
656
496
            Self::Integer(value) => *value as f64,
657
744
            Self::Float(value) => *value,
658
        }
659
1736
    }
660
}
661

            
662
/// A conversion between numeric types wasn't supported.
663
#[derive(thiserror::Error, Debug)]
664
#[error("incompatible numeric type")]
665
pub struct IncompatibleTypeError;
666

            
667
impl From<f64> for Numeric {
668
2728
    fn from(value: f64) -> Self {
669
2728
        Self::Float(value)
670
2728
    }
671
}
672

            
673
impl From<i64> for Numeric {
674
3968
    fn from(value: i64) -> Self {
675
3968
        Self::Integer(value)
676
3968
    }
677
}
678

            
679
impl From<u64> for Numeric {
680
2483503
    fn from(value: u64) -> Self {
681
2483503
        Self::UnsignedInteger(value)
682
2483503
    }
683
}
684

            
685
#[allow(clippy::fallible_impl_from)]
686
impl TryFrom<Numeric> for f64 {
687
    type Error = IncompatibleTypeError;
688
    fn try_from(value: Numeric) -> Result<Self, IncompatibleTypeError> {
689
992
        if let Numeric::Float(value) = value {
690
992
            Ok(value)
691
        } else {
692
            Err(IncompatibleTypeError)
693
        }
694
992
    }
695
}
696

            
697
#[allow(clippy::fallible_impl_from)]
698
impl TryFrom<Numeric> for u64 {
699
    type Error = IncompatibleTypeError;
700
    fn try_from(value: Numeric) -> Result<Self, IncompatibleTypeError> {
701
2482263
        if let Numeric::UnsignedInteger(value) = value {
702
2482263
            Ok(value)
703
        } else {
704
            Err(IncompatibleTypeError)
705
        }
706
2482263
    }
707
}
708

            
709
#[allow(clippy::fallible_impl_from)]
710
impl TryFrom<Numeric> for i64 {
711
    type Error = IncompatibleTypeError;
712
    fn try_from(value: Numeric) -> Result<Self, IncompatibleTypeError> {
713
1488
        if let Numeric::Integer(value) = value {
714
1488
            Ok(value)
715
        } else {
716
            Err(IncompatibleTypeError)
717
        }
718
1488
    }
719
}
720

            
721
/// The result of a [`KeyOperation`].
722
1151742
#[derive(Clone, Serialize, Deserialize, Debug)]
723
pub enum Output {
724
    /// A status was returned.
725
    Status(KeyStatus),
726
    /// A value was returned.
727
    Value(Option<Value>),
728
}
729
/// The status of an operation on a Key.
730
5075
#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq)]
731
pub enum KeyStatus {
732
    /// A new key was inserted.
733
    Inserted,
734
    /// An existing key was updated with a new value.
735
    Updated,
736
    /// A key was deleted.
737
    Deleted,
738
    /// No changes were made.
739
    NotChanged,
740
}