bonsaidb_core/
keyvalue.rs

1use arc_bytes::serde::Bytes;
2use serde::{Deserialize, Serialize};
3
4mod timestamp;
5
6pub use self::timestamp::Timestamp;
7use crate::Error;
8
9mod 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        fn set_key<'a, S: Into<String>, V: Serialize + Send + Sync>(
59            &'a self,
60            key: S,
61            value: &'a V,
62        ) -> set::Builder<'a, Self, V> {
63            set::Builder::new(
64                self,
65                self.key_namespace().map(Into::into),
66                key.into(),
67                PendingValue::Serializeable(value),
68            )
69        }
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        fn set_numeric_key<S: Into<String>, V: Into<Numeric>>(
92            &self,
93            key: S,
94            value: V,
95        ) -> set::Builder<'_, Self, ()> {
96            set::Builder::new(
97                self,
98                self.key_namespace().map(Into::into),
99                key.into(),
100                PendingValue::Numeric(value.into()),
101            )
102        }
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        fn increment_key_by<
110            S: Into<String> + Send + Sync,
111            V: Into<Numeric> + TryFrom<Numeric, Error = IncompatibleTypeError> + Send + Sync,
112        >(
113            &self,
114            key: S,
115            value: V,
116        ) -> increment::Builder<'_, Self, V> {
117            increment::Builder::new(
118                self,
119                self.key_namespace().map(Into::into),
120                true,
121                key.into(),
122                value.into(),
123            )
124        }
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        fn decrement_key_by<
132            S: Into<String> + Send + Sync,
133            V: Into<Numeric> + TryFrom<Numeric, Error = IncompatibleTypeError> + Send + Sync,
134        >(
135            &self,
136            key: S,
137            value: V,
138        ) -> increment::Builder<'_, Self, V> {
139            increment::Builder::new(
140                self,
141                self.key_namespace().map(Into::into),
142                false,
143                key.into(),
144                value.into(),
145            )
146        }
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        fn get_key<S: Into<String>>(&'_ self, key: S) -> get::Builder<'_, Self> {
152            get::Builder::new(self, self.key_namespace().map(Into::into), key.into())
153        }
154
155        /// Deletes the value stored at `key`.
156        fn delete_key<S: Into<String> + Send>(&'_ self, key: S) -> Result<KeyStatus, Error> {
157            match self.execute_key_operation(KeyOperation {
158                namespace: self.key_namespace().map(ToOwned::to_owned),
159                key: key.into(),
160                command: Command::Delete,
161            })? {
162                Output::Status(status) => Ok(status),
163                Output::Value(_) => unreachable!("invalid output from delete operation"),
164            }
165        }
166
167        /// The current namespace.
168        fn key_namespace(&self) -> Option<&'_ str> {
169            None
170        }
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        fn with_key_namespace(&'_ self, namespace: &str) -> Namespaced<'_, Self> {
176            Namespaced::new(namespace.to_string(), self)
177        }
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        fn set_key<'a, S: Into<String>, V: Serialize + Send + Sync>(
210            &'a self,
211            key: S,
212            value: &'a V,
213        ) -> set::AsyncBuilder<'a, Self, V> {
214            set::AsyncBuilder::new(
215                self,
216                self.key_namespace().map(Into::into),
217                key.into(),
218                PendingValue::Serializeable(value),
219            )
220        }
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        fn set_binary_key<'a, S: Into<String>>(
226            &'a self,
227            key: S,
228            bytes: &'a [u8],
229        ) -> set::AsyncBuilder<'a, Self, ()> {
230            set::AsyncBuilder::new(
231                self,
232                self.key_namespace().map(Into::into),
233                key.into(),
234                PendingValue::Bytes(bytes),
235            )
236        }
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        fn set_numeric_key<S: Into<String>, V: Into<Numeric>>(
243            &self,
244            key: S,
245            value: V,
246        ) -> set::AsyncBuilder<'_, Self, ()> {
247            set::AsyncBuilder::new(
248                self,
249                self.key_namespace().map(Into::into),
250                key.into(),
251                PendingValue::Numeric(value.into()),
252            )
253        }
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        fn increment_key_by<
261            S: Into<String> + Send + Sync,
262            V: Into<Numeric> + TryFrom<Numeric, Error = IncompatibleTypeError> + Send + Sync,
263        >(
264            &self,
265            key: S,
266            value: V,
267        ) -> increment::AsyncBuilder<'_, Self, V> {
268            increment::AsyncBuilder::new(
269                self,
270                self.key_namespace().map(Into::into),
271                true,
272                key.into(),
273                value.into(),
274            )
275        }
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        fn decrement_key_by<
283            S: Into<String> + Send + Sync,
284            V: Into<Numeric> + TryFrom<Numeric, Error = IncompatibleTypeError> + Send + Sync,
285        >(
286            &self,
287            key: S,
288            value: V,
289        ) -> increment::AsyncBuilder<'_, Self, V> {
290            increment::AsyncBuilder::new(
291                self,
292                self.key_namespace().map(Into::into),
293                false,
294                key.into(),
295                value.into(),
296            )
297        }
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        fn get_key<S: Into<String>>(&'_ self, key: S) -> get::AsyncBuilder<'_, Self> {
303            get::AsyncBuilder::new(self, self.key_namespace().map(Into::into), key.into())
304        }
305
306        /// Deletes the value stored at `key`.
307        async fn delete_key<S: Into<String> + Send>(&'_ self, key: S) -> Result<KeyStatus, Error> {
308            match self
309                .execute_key_operation(KeyOperation {
310                    namespace: self.key_namespace().map(ToOwned::to_owned),
311                    key: key.into(),
312                    command: Command::Delete,
313                })
314                .await?
315            {
316                Output::Status(status) => Ok(status),
317                Output::Value(_) => unreachable!("invalid output from delete operation"),
318            }
319        }
320
321        /// The current namespace.
322        fn key_namespace(&self) -> Option<&'_ str> {
323            None
324        }
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        fn with_key_namespace(&'_ self, namespace: &str) -> Namespaced<'_, Self> {
330            Namespaced::new(namespace.to_string(), self)
331        }
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        fn prepare(self) -> Result<Value, Error> {
351            match self {
352                Self::Bytes(bytes) => Ok(Value::Bytes(Bytes::from(bytes))),
353                Self::Serializeable(value) => Ok(Value::Bytes(Bytes::from(pot::to_vec(value)?))),
354                Self::Numeric(numeric) => Ok(Value::Numeric(numeric)),
355            }
356        }
357    }
358}
359
360pub use implementation::*;
361
362/// Checks for existing keys.
363#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
364pub 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#[derive(Clone, Serialize, Deserialize, Debug)]
372/// An operation performed on a key.
373pub 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#[derive(Clone, Serialize, Deserialize, Debug)]
384pub 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#[derive(Clone, Serialize, Deserialize, Debug)]
420pub 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#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
437pub enum Value {
438    /// A value stored as a byte array.
439    Bytes(Bytes),
440    /// A numeric value.
441    Numeric(Numeric),
442}
443
444impl Value {
445    /// Validates this value to ensure it is safe to store.
446    pub fn validate(self) -> Result<Self, Error> {
447        match self {
448            Self::Numeric(numeric) => numeric.validate().map(Self::Numeric),
449            Self::Bytes(vec) => Ok(Self::Bytes(vec)),
450        }
451    }
452
453    /// Deserializes the bytes contained inside of this value. Returns an error
454    /// if this value doesn't contain bytes.
455    pub fn deserialize<V: for<'de> Deserialize<'de>>(&self) -> Result<V, Error> {
456        match self {
457            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    }
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    pub fn as_i64_lossy(&self, saturating: bool) -> Option<i64> {
468        match self {
469            Self::Bytes(_) => None,
470            Self::Numeric(value) => Some(value.as_i64_lossy(saturating)),
471        }
472    }
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    pub fn as_u64_lossy(&self, saturating: bool) -> Option<u64> {
477        match self {
478            Self::Bytes(_) => None,
479            Self::Numeric(value) => Some(value.as_u64_lossy(saturating)),
480        }
481    }
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    pub const fn as_f64_lossy(&self) -> Option<f64> {
486        match self {
487            Self::Bytes(_) => None,
488            Self::Numeric(value) => Some(value.as_f64_lossy()),
489        }
490    }
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    pub fn as_i64(&self) -> Option<i64> {
495        match self {
496            Self::Bytes(_) => None,
497            Self::Numeric(value) => value.as_i64(),
498        }
499    }
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    pub fn as_u64(&self) -> Option<u64> {
504        match self {
505            Self::Bytes(_) => None,
506            Self::Numeric(value) => value.as_u64(),
507        }
508    }
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    pub const fn as_f64(&self) -> Option<f64> {
513        match self {
514            Self::Bytes(_) => None,
515            Self::Numeric(value) => value.as_f64(),
516        }
517    }
518}
519
520/// A numerical value.
521#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
522pub 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
531impl 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    pub fn validate(self) -> Result<Self, Error> {
539        if let Self::Float(float) = self {
540            if float.is_nan() {
541                return Err(Error::NotANumber);
542            }
543        }
544
545        Ok(self)
546    }
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    pub fn as_i64(&self) -> Option<i64> {
553        match self {
554            Self::Integer(value) => Some(*value),
555            Self::UnsignedInteger(value) => (*value).try_into().ok(),
556            Self::Float(value) => {
557                if value.fract().abs() > 0. {
558                    None
559                } else {
560                    Some(*value as i64)
561                }
562            }
563        }
564    }
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    pub fn as_i64_lossy(&self, saturating: bool) -> i64 {
572        match self {
573            Self::Integer(value) => *value,
574            Self::UnsignedInteger(value) => {
575                if saturating {
576                    (*value).try_into().unwrap_or(i64::MAX)
577                } else {
578                    *value as i64
579                }
580            }
581            Self::Float(value) => *value as i64,
582        }
583    }
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    pub fn as_u64(&self) -> Option<u64> {
590        match self {
591            Self::UnsignedInteger(value) => Some(*value),
592            Self::Integer(value) => (*value).try_into().ok(),
593            Self::Float(value) => {
594                if value.fract() < f64::EPSILON && value.is_sign_positive() {
595                    Some(*value as u64)
596                } else {
597                    None
598                }
599            }
600        }
601    }
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    pub fn as_u64_lossy(&self, saturating: bool) -> u64 {
609        match self {
610            Self::UnsignedInteger(value) => *value,
611            Self::Integer(value) => {
612                if saturating {
613                    (*value).try_into().unwrap_or(0)
614                } else {
615                    *value as u64
616                }
617            }
618            Self::Float(value) => *value as u64,
619        }
620    }
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    pub const fn as_f64(&self) -> Option<f64> {
627        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            Self::Integer(value) => {
636                if *value > 2_i64.pow(f64::MANTISSA_DIGITS)
637                    || *value < -(2_i64.pow(f64::MANTISSA_DIGITS))
638                {
639                    None
640                } else {
641                    Some(*value as f64)
642                }
643            }
644            Self::Float(value) => Some(*value),
645        }
646    }
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    pub const fn as_f64_lossy(&self) -> f64 {
653        match self {
654            Self::UnsignedInteger(value) => *value as f64,
655            Self::Integer(value) => *value as f64,
656            Self::Float(value) => *value,
657        }
658    }
659}
660
661/// A conversion between numeric types wasn't supported.
662#[derive(thiserror::Error, Debug)]
663#[error("incompatible numeric type")]
664pub struct IncompatibleTypeError;
665
666impl From<f64> for Numeric {
667    fn from(value: f64) -> Self {
668        Self::Float(value)
669    }
670}
671
672impl From<i64> for Numeric {
673    fn from(value: i64) -> Self {
674        Self::Integer(value)
675    }
676}
677
678impl From<u64> for Numeric {
679    fn from(value: u64) -> Self {
680        Self::UnsignedInteger(value)
681    }
682}
683
684#[allow(clippy::fallible_impl_from)]
685impl TryFrom<Numeric> for f64 {
686    type Error = IncompatibleTypeError;
687
688    fn try_from(value: Numeric) -> Result<Self, IncompatibleTypeError> {
689        if let Numeric::Float(value) = value {
690            Ok(value)
691        } else {
692            Err(IncompatibleTypeError)
693        }
694    }
695}
696
697#[allow(clippy::fallible_impl_from)]
698impl TryFrom<Numeric> for u64 {
699    type Error = IncompatibleTypeError;
700
701    fn try_from(value: Numeric) -> Result<Self, IncompatibleTypeError> {
702        if let Numeric::UnsignedInteger(value) = value {
703            Ok(value)
704        } else {
705            Err(IncompatibleTypeError)
706        }
707    }
708}
709
710#[allow(clippy::fallible_impl_from)]
711impl TryFrom<Numeric> for i64 {
712    type Error = IncompatibleTypeError;
713
714    fn try_from(value: Numeric) -> Result<Self, IncompatibleTypeError> {
715        if let Numeric::Integer(value) = value {
716            Ok(value)
717        } else {
718            Err(IncompatibleTypeError)
719        }
720    }
721}
722
723/// The result of a [`KeyOperation`].
724#[derive(Clone, Serialize, Deserialize, Debug)]
725pub 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#[derive(Copy, Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
733pub 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}