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