use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::io;
use crate::store::persistence::file_handling::write_store_sync;
use crate::store::InnerStore;
#[derive(Default, Serialize, Deserialize)]
pub struct MemStore {
pub strings: InnerStore<String>,
pub counters: InnerStore<isize>,
}
impl MemStore {
pub fn new() -> Self {
Self::default()
}
pub fn insert_string(&mut self, key: &str, value: &str) -> io::Result<()> {
self.strings.insert(key, value.to_string())
}
pub fn get_string(&self, key: &str) -> io::Result<String> {
self.strings.retrieve(key)
}
pub fn remove_string(&mut self, key: &str) -> io::Result<String> {
self.strings.remove(key)
}
pub fn clear_strings(&mut self) -> io::Result<()> {
self.strings.clear()
}
pub fn get_string_store_ref(&self) -> &HashMap<String, String> {
self.strings.get_ref()
}
pub fn incr(&mut self, key: impl AsRef<str>) -> io::Result<isize> {
self.counters
.inner
.entry(key.as_ref().to_string())
.and_modify(|count| *count += 1)
.or_insert(1);
let count = self.counters.retrieve(key.as_ref())?;
Ok(count)
}
pub fn decr(&mut self, key: impl AsRef<str>) -> io::Result<isize> {
self.counters
.inner
.entry(key.as_ref().to_string())
.and_modify(|count| *count -= 1)
.or_insert(-1);
let count = self.counters.retrieve(key.as_ref())?;
Ok(count)
}
pub fn dump_store(&self, filepath: impl AsRef<std::path::Path>) -> io::Result<()> {
write_store_sync(filepath, self)
}
}
#[cfg(test)]
mod memstore {
use super::*;
use std::path::PathBuf;
use tempdir::TempDir;
fn create_test_directory() -> io::Result<PathBuf> {
let td = TempDir::new("teststore")?;
Ok(td.path().to_path_buf())
}
#[test]
fn empty() {
let ms = MemStore::new();
let strings = ms.strings.get_ref();
assert_eq!(strings.len(), 0);
}
#[test]
fn string_store_add_entries() -> io::Result<()> {
let mut ms = MemStore::new();
ms.insert_string("key1", "value1")?;
assert_eq!(ms.strings.len(), 1);
ms.insert_string("key2", "value2")?;
assert_eq!(ms.strings.len(), 2);
Ok(())
}
#[test]
fn string_store_add_loads_of_entries() -> io::Result<()> {
let mut ms = MemStore::new();
for i in 0..100_000 {
let key = format!("key-{}", i);
let value = format!("value-{}", i);
ms.insert_string(&key, &value)?;
}
assert!(ms.strings.len() == 100_000);
Ok(())
}
#[test]
fn string_store_get_entries() -> io::Result<()> {
let mut ms = MemStore::new();
ms.insert_string("key1", "value1")?;
ms.insert_string("key2", "value2")?;
let mut result = ms.get_string("key2")?;
assert_eq!(result, "value2");
result = ms.get_string("key1")?;
assert_eq!(result, "value1");
Ok(())
}
#[test]
fn string_store_no_entries() -> io::Result<()> {
let ms = MemStore::new();
let result = ms.get_string("key1")?;
assert_eq!(result, "");
Ok(())
}
#[test]
fn clear_string_store() -> io::Result<()> {
let mut ms = MemStore::new();
for i in 0..1000 {
let key = format!("key-{}", i);
ms.insert_string(&key, "value")?;
}
assert!(ms.strings.len() == 1000);
ms.clear_strings()?;
assert!(ms.strings.len() == 0);
Ok(())
}
#[test]
fn incrementer() -> io::Result<()> {
let mut ms = MemStore::new();
let mut value = 0;
for _ in 0..1000 {
value = ms.incr("view-counter")?;
}
assert_eq!(value, 1000);
let stored_value = ms.counters.retrieve("view-counter")?;
assert_eq!(value, stored_value);
Ok(())
}
#[test]
fn incrementer_multiple_keys() -> io::Result<()> {
let mut ms = MemStore::new();
for _ in 0..3 {
ms.incr("key-1")?;
}
for _ in 0..5 {
ms.incr("key-2")?;
}
let key1 = ms.counters.retrieve("key-1")?;
let key2 = ms.counters.retrieve("key-2")?;
assert_eq!(key1, 3);
assert_eq!(key2, 5);
Ok(())
}
#[test]
fn decrementer() -> io::Result<()> {
let mut ms = MemStore::new();
let mut value = 0;
for _ in 0..1000 {
value = ms.decr("view-counter")?;
}
assert_eq!(value, -1000);
let stored_value = ms.counters.retrieve("view-counter")?;
assert_eq!(value, stored_value);
Ok(())
}
#[test]
fn decrementer_multiple_keys() -> io::Result<()> {
let mut ms = MemStore::new();
for _ in 0..3 {
ms.decr("key-1")?;
}
for _ in 0..5 {
ms.decr("key-2")?;
}
let key1 = ms.counters.retrieve("key-1")?;
let key2 = ms.counters.retrieve("key-2")?;
assert_eq!(key1, -3);
assert_eq!(key2, -5);
Ok(())
}
#[test]
fn dump_store_to_disk() -> io::Result<()> {
let td = create_test_directory()?;
let rubinstore = td.join("rubinstore.json");
std::fs::create_dir_all(td)?;
let mut ms = MemStore::new();
ms.insert_string("key1", "value1")?;
ms.dump_store(&rubinstore)?;
assert!(rubinstore.exists());
Ok(())
}
}