use crate::{CrdtMerge, LWWRegister};
use fxhash::FxHashMap;
use serde::{Deserialize, Serialize};
use std::hash::Hash;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct LWWMap<K: Hash + Eq + Clone, V: Clone> {
map: FxHashMap<K, LWWRegister<Option<V>>>,
}
impl<K: Hash + Eq + Clone, V: Clone> Default for LWWMap<K, V> {
fn default() -> Self {
Self {
map: FxHashMap::default(),
}
}
}
impl<K: Hash + Eq + Clone, V: Clone> LWWMap<K, V> {
pub fn new() -> Self {
Self::default()
}
fn empty_register() -> LWWRegister<Option<V>> {
LWWRegister::new(None, -1)
}
pub fn put(&mut self, key: K, value: V, timestamp: i64) {
let register = self.map.entry(key).or_insert_with(Self::empty_register);
register.set(Some(value), timestamp);
}
pub fn remove(&mut self, key: &K, timestamp: i64) {
let register = self
.map
.entry(key.clone())
.or_insert_with(Self::empty_register);
register.set(None, timestamp);
}
pub fn get(&self, key: &K) -> Option<&V> {
self.map.get(key).and_then(|reg| reg.get().as_ref())
}
pub fn keys(&self) -> impl Iterator<Item = &K> {
self.map
.iter()
.filter(|(_, reg)| reg.get().is_some())
.map(|(k, _)| k)
}
pub fn len(&self) -> usize {
self.map.values().filter(|reg| reg.get().is_some()).count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<K: Hash + Eq + Clone, V: Clone + Serialize> CrdtMerge for LWWMap<K, V> {
fn merge(&mut self, other: &Self) {
for (key, other_register) in &other.map {
let register = self
.map
.entry(key.clone())
.or_insert_with(Self::empty_register);
register.merge(other_register);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_put_get() {
let mut map = LWWMap::new();
map.put("a".to_string(), 1, 100);
map.put("b".to_string(), 2, 110);
assert_eq!(map.get(&"a".to_string()), Some(&1));
assert_eq!(map.get(&"b".to_string()), Some(&2));
map.put("a".to_string(), 3, 105);
assert_eq!(map.get(&"a".to_string()), Some(&3));
}
#[test]
fn test_remove() {
let mut map = LWWMap::new();
map.put("a".to_string(), 1, 100);
map.remove(&"a".to_string(), 110);
assert_eq!(map.get(&"a".to_string()), None);
map.put("a".to_string(), 2, 105);
assert_eq!(map.get(&"a".to_string()), None); }
#[test]
fn test_merge() {
let mut a = LWWMap::new();
a.put("a".to_string(), 1, 100);
let mut b = LWWMap::new();
b.put("a".to_string(), 2, 110);
b.put("b".to_string(), 3, 100);
a.merge(&b);
assert_eq!(a.get(&"a".to_string()), Some(&2));
assert_eq!(a.get(&"b".to_string()), Some(&3));
}
}