From 31a762827251f80496cefac09eaff91075b5501b Mon Sep 17 00:00:00 2001 From: Yaroslav Dynnikov <yaroslav.dynnikov@gmail.com> Date: Fri, 16 Sep 2022 02:56:23 +0300 Subject: [PATCH] feature: KVCell data strcture `KVCell` contains an optional value and provides access by key in two flavors: ```rust fn take_or_drop(&mut self, key: &K) -> Option<T> fn take_or_keep(&mut self, key: &K) -> Option<T> ``` Both return `Some` value only if provided key matches the contained one. The behavior differs when the provided key doesn't match: - `take_or_drop` always leaves the cell empty. It supersedes `CachedCell` and copies its behavior. - `take_or_keep` retains contained value. It's a replacement for `JointStateLatch`. --- src/kvcell.rs | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + 2 files changed, 108 insertions(+) create mode 100644 src/kvcell.rs diff --git a/src/kvcell.rs b/src/kvcell.rs new file mode 100644 index 0000000000..50535e9453 --- /dev/null +++ b/src/kvcell.rs @@ -0,0 +1,107 @@ +//! Container for a single key-value pair. + +/// A cell containing optional value accessed by key. +#[derive(Default)] +pub struct KVCell<K, T> { + content: Option<(K, T)>, +} + +impl<K, T> KVCell<K, T> +where + K: PartialEq, +{ + /// Creates an empty `KVCell`. + #[allow(dead_code)] + pub fn new() -> Self { + Self { content: None } + } + + /// Returns `true` if cell contains no value. + #[allow(dead_code)] + pub fn is_empty(&self) -> bool { + self.content.is_none() + } + + /// Sets the contained key and value, then returns mutable reference + /// to the value. + /// + /// If the cell already contains some value, the old one is dropped. + #[allow(dead_code)] + pub fn insert(&mut self, key: K, value: T) -> &mut T { + &mut self.content.insert((key, value)).1 + } + + /// Takes the value out of the cell (if some) regardless of the + /// contained key. The cell becomes empty. + #[allow(dead_code)] + pub fn take(&mut self) -> Option<T> { + self.content.take().map(|(_, v)| v) + } + + /// If `key` matches the contained one, takes the value out of the + /// cell, leaving it empty. + /// + /// Otherwise, if `key` doesn't match, retains the old value and key + /// and returns `None`. + #[allow(dead_code)] + pub fn take_or_keep(&mut self, key: &K) -> Option<T> { + if matches!(&self.content, Some((k, _)) if k == key) { + self.content.take().map(|(_, v)| v) + } else { + None + } + } + + /// If `key` matches the contained one, takes the value out of the + /// cell, leaving it empty. + /// + /// Otherwise drops the contained value and returns `None`. + /// + /// Anyway, the cell becomes empty. + #[must_use] + #[allow(dead_code)] + pub fn take_or_drop(&mut self, key: &K) -> Option<T> { + match self.content.take() { + Some((k, v)) if &k == key => Some(v), + Some(_) => None, + None => None, + } + } +} + +#[cfg(test)] +mod tests { + type KVCell = super::KVCell<u8, u32>; + + #[test] + fn kvcell() { + let mut kv = KVCell::new(); + assert!(kv.is_empty()); + + kv.insert(1, 101); + assert_eq!(kv.is_empty(), false); + assert_eq!(kv.take(), Some(101)); + assert_eq!(kv.is_empty(), true); + assert_eq!(kv.take(), None); + + kv.insert(2, 102); + assert_eq!(kv.take_or_drop(&2), Some(102)); + assert_eq!(kv.is_empty(), true); + + kv.insert(3, 103); + assert_eq!(kv.take_or_drop(&0), None); + assert_eq!(kv.is_empty(), true); + + kv.insert(4, 104); + assert_eq!(kv.take_or_keep(&4), Some(104)); + assert_eq!(kv.is_empty(), true); + + kv.insert(5, 105); + assert_eq!(kv.take_or_keep(&0), None); + assert_eq!(kv.is_empty(), false); + + let v: &mut u32 = kv.insert(6, 106); + *v = 666; + assert_eq!(kv.take(), Some(666)); + } +} diff --git a/src/main.rs b/src/main.rs index 9b47e48c29..6d8a8a1ac4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,6 +26,7 @@ mod args; mod cache; mod discovery; mod ipc; +mod kvcell; mod mailbox; mod tarantool; mod tlog; -- GitLab