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::keyvalue::{Command, KeyCheck, KeyOperation, KeyStatus, Output, Timestamp};
16
    use crate::Error;
17

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

            
27
    use namespaced::Namespaced;
28

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
360
pub use implementation::*;
361

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
545
3211960
        Ok(self)
546
3212600
    }
547

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

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

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

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

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

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

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

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

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

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

            
684
#[allow(clippy::fallible_impl_from)]
685
impl TryFrom<Numeric> for f64 {
686
    type Error = IncompatibleTypeError;
687

            
688
1280
    fn try_from(value: Numeric) -> Result<Self, IncompatibleTypeError> {
689
1280
        if let Numeric::Float(value) = value {
690
1280
            Ok(value)
691
        } else {
692
            Err(IncompatibleTypeError)
693
        }
694
1280
    }
695
}
696

            
697
#[allow(clippy::fallible_impl_from)]
698
impl TryFrom<Numeric> for u64 {
699
    type Error = IncompatibleTypeError;
700

            
701
3202920
    fn try_from(value: Numeric) -> Result<Self, IncompatibleTypeError> {
702
3202920
        if let Numeric::UnsignedInteger(value) = value {
703
3202920
            Ok(value)
704
        } else {
705
            Err(IncompatibleTypeError)
706
        }
707
3202920
    }
708
}
709

            
710
#[allow(clippy::fallible_impl_from)]
711
impl TryFrom<Numeric> for i64 {
712
    type Error = IncompatibleTypeError;
713

            
714
1920
    fn try_from(value: Numeric) -> Result<Self, IncompatibleTypeError> {
715
1920
        if let Numeric::Integer(value) = value {
716
1920
            Ok(value)
717
        } else {
718
            Err(IncompatibleTypeError)
719
        }
720
1920
    }
721
}
722

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