#![allow(dead_code, unused_imports, unused_variables)]
use crate::kv_store::entry::Entry;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
use std::path::Path;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum KvStoreError {
KeyNotFound(String),
KeyAlreadyExists(String),
StorageError(String),
SerializationError(String),
}
impl std::fmt::Display for KvStoreError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
KvStoreError::KeyNotFound(key) => write!(f, "Key not found: {}", key),
KvStoreError::KeyAlreadyExists(key) => write!(f, "Key already exists: {}", key),
KvStoreError::StorageError(msg) => write!(f, "Storage error: {}", msg),
KvStoreError::SerializationError(msg) => write!(f, "Serialization error: {}", msg),
}
}
}
impl std::error::Error for KvStoreError {}
pub type Result<T> = std::result::Result<T, KvStoreError>;
#[derive(Debug, Clone)]
pub struct KvStore {
pub(crate) data: HashMap<String, Entry>,
path: Option<String>,
}
impl KvStore {
pub fn new() -> Self {
Self {
data: HashMap::new(),
path: None,
}
}
pub fn with_path<P: AsRef<Path>>(path: P) -> Result<Self> {
let path_str = path.as_ref().to_string_lossy().to_string();
if path.as_ref().exists() {
match Self::load(path.as_ref()) {
Ok(store) => Ok(store),
Err(_) => {
Ok(Self {
data: HashMap::new(),
path: Some(path_str),
})
}
}
} else {
let store = Self {
data: HashMap::new(),
path: Some(path_str),
};
store.save()?;
Ok(store)
}
}
pub fn insert(&mut self, key: impl Into<String>, value: impl Into<String>) -> Result<()> {
let key = key.into();
if self.data.contains_key(&key) {
return Err(KvStoreError::KeyAlreadyExists(key));
}
let entry = Entry::new(key.clone(), value);
self.data.insert(key.clone(), entry);
if let Some(ref path) = self.path {
self.save()?;
}
Ok(())
}
pub fn upsert(&mut self, key: impl Into<String>, value: impl Into<String>) -> Result<()> {
let key = key.into();
let entry = Entry::new(key.clone(), value);
self.data.insert(key.clone(), entry);
if let Some(ref path) = self.path {
self.save()?;
}
Ok(())
}
pub fn upsert_entry(&mut self, key: impl Into<String>, entry: Entry) -> Result<()> {
let key = key.into();
self.data.insert(key.clone(), entry);
if let Some(ref path) = self.path {
self.save()?;
}
Ok(())
}
pub fn get(&self, key: &str) -> Result<String> {
match self.data.get(key) {
Some(entry) => Ok(entry.value.clone()),
None => Err(KvStoreError::KeyNotFound(key.to_string())),
}
}
pub fn get_entry(&self, key: &str) -> Result<&Entry> {
match self.data.get(key) {
Some(entry) => Ok(entry),
None => Err(KvStoreError::KeyNotFound(key.to_string())),
}
}
pub fn contains_key(&self, key: &str) -> bool {
self.data.contains_key(key)
}
pub fn remove(&mut self, key: &str) -> Result<String> {
match self.data.remove(key) {
Some(entry) => {
let value = entry.value.clone();
if let Some(ref path) = self.path {
self.save()?;
}
Ok(value)
}
None => Err(KvStoreError::KeyNotFound(key.to_string())),
}
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn clear(&mut self) -> Result<()> {
self.data.clear();
if let Some(ref path) = self.path {
self.save()?;
}
Ok(())
}
pub fn save(&self) -> Result<()> {
if let Some(ref path) = self.path {
let serializable_data: HashMap<String, String> = self
.data
.iter()
.map(|(k, v)| (k.clone(), v.value.clone()))
.collect();
match serde_json::to_string_pretty(&serializable_data) {
Ok(json) => {
if let Err(e) = fs::write(path, &json) {
return Err(KvStoreError::StorageError(format!(
"Failed to write to {}: {}",
path, e
)));
}
Ok(())
}
Err(e) => Err(KvStoreError::SerializationError(format!(
"Failed to serialize store: {}",
e
))),
}
} else {
Err(KvStoreError::StorageError(
"No path configured for persistence".to_string(),
))
}
}
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
match fs::read_to_string(path.as_ref()) {
Ok(json) => match serde_json::from_str::<HashMap<String, String>>(&json) {
Ok(data) => {
let store_data: HashMap<String, Entry> = data
.into_iter()
.map(|(k, v)| (k.clone(), Entry::new(k.clone(), v)))
.collect();
Ok(Self {
data: store_data,
path: Some(path.as_ref().to_string_lossy().to_string()),
})
}
Err(e) => Err(KvStoreError::SerializationError(format!(
"Failed to deserialize store: {}",
e
))),
},
Err(e) => Err(KvStoreError::StorageError(format!(
"Failed to read from {}: {}",
path.as_ref().display(),
e
))),
}
}
pub fn keys(&self) -> Vec<String> {
self.data.keys().cloned().collect()
}
pub fn entries(&self) -> Vec<&Entry> {
self.data.values().collect()
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &Entry)> {
self.data.iter()
}
}
impl Default for KvStore {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::tempdir;
#[test]
fn test_new_store_is_empty() {
let store = KvStore::new();
assert!(store.is_empty());
assert_eq!(store.len(), 0);
}
#[test]
fn test_insert_and_get() {
let mut store = KvStore::new();
store.insert("key1", "value1").unwrap();
assert_eq!(store.get("key1").unwrap(), "value1");
assert_eq!(store.len(), 1);
}
#[test]
fn test_insert_duplicate_key() {
let mut store = KvStore::new();
store.insert("key1", "value1").unwrap();
assert!(store.insert("key1", "value2").is_err());
assert_eq!(store.get("key1").unwrap(), "value1");
}
#[test]
fn test_upsert() {
let mut store = KvStore::new();
store.insert("key1", "value1").unwrap();
store.upsert("key1", "value2").unwrap();
assert_eq!(store.get("key1").unwrap(), "value2");
}
#[test]
fn test_remove() {
let mut store = KvStore::new();
store.insert("key1", "value1").unwrap();
assert_eq!(store.remove("key1").unwrap(), "value1");
assert!(store.get("key1").is_err());
assert_eq!(store.len(), 0);
}
#[test]
fn test_contains_key() {
let mut store = KvStore::new();
store.insert("key1", "value1").unwrap();
assert!(store.contains_key("key1"));
assert!(!store.contains_key("key2"));
}
#[test]
fn test_get_nonexistent_key() {
let store = KvStore::new();
assert!(store.get("nonexistent").is_err());
}
#[test]
fn test_remove_nonexistent_key() {
let mut store = KvStore::new();
assert!(store.remove("nonexistent").is_err());
}
#[test]
fn test_clear() {
let mut store = KvStore::new();
store.insert("key1", "value1").unwrap();
store.insert("key2", "value2").unwrap();
store.clear().unwrap();
assert!(store.is_empty());
assert_eq!(store.len(), 0);
}
#[test]
fn test_persistence() {
let dir = tempdir().unwrap();
let path = dir.path().join("store.json");
let mut store = KvStore::with_path(&path).unwrap();
store.insert("key1", "value1").unwrap();
store.insert("key2", "value2").unwrap();
let store2 = KvStore::with_path(&path).unwrap();
assert_eq!(store2.get("key1").unwrap(), "value1");
assert_eq!(store2.get("key2").unwrap(), "value2");
}
#[test]
fn test_keys() {
let mut store = KvStore::new();
store.insert("key1", "value1").unwrap();
store.insert("key2", "value2").unwrap();
let keys = store.keys();
assert!(keys.contains(&"key1".to_string()));
assert!(keys.contains(&"key2".to_string()));
}
#[test]
fn test_entries() {
let mut store = KvStore::new();
store.insert("key1", "value1").unwrap();
store.insert("key2", "value2").unwrap();
let entries = store.entries();
assert_eq!(entries.len(), 2);
}
}