use std::collections::HashMap;
use std::fs;
use std::path::Path;
use bincode::{serialize, deserialize};
use std::io::{Error, ErrorKind};
#[derive(Debug)]
pub struct KvStore {
keys: HashMap<String, String>,
storage_path: String,
}
impl KvStore {
pub fn new(storage_path: &str) -> Result<KvStore, Error> {
fs::create_dir_all(storage_path)?;
Ok(KvStore {
keys: HashMap::new(),
storage_path: storage_path.to_string(),
})
}
pub fn set(&mut self, key: String, value: String) -> Result<(), Error> {
let file_path = format!("{}/{}.bin", self.storage_path, key);
let encoded: Vec<u8> = serialize(&value).map_err(|e| Error::new(ErrorKind::Other, e))?;
fs::write(file_path, encoded)?;
self.keys.insert(key, value);
Ok(())
}
pub fn get(&self, key: &str) -> Result<Option<String>, Error> {
if let Some(value) = self.keys.get(key) {
return Ok(Some(value.clone()));
}
let file_path = format!("{}/{}.bin", self.storage_path, key);
if !Path::new(&file_path).exists() {
return Ok(None);
}
let data = fs::read(file_path)?;
let decoded: String = deserialize(&data).map_err(|e| Error::new(ErrorKind::Other, e))?;
Ok(Some(decoded))
}
pub fn remove(&mut self, key: &str) -> Result<(), Error> {
let file_path = format!("{}/{}.bin", self.storage_path, key);
if Path::new(&file_path).exists() {
fs::remove_file(file_path)?;
}
self.keys.remove(key);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
#[test]
fn test_set_get() {
let dir = tempdir().unwrap();
let mut store = KvStore::new(dir.path().to_str().unwrap()).unwrap();
store.set("key1".to_string(), "value1".to_string()).unwrap();
assert_eq!(store.get("key1").unwrap(), Some("value1".to_string()));
}
#[test]
fn test_get_non_existent() {
let dir = tempdir().unwrap();
let store = KvStore::new(dir.path().to_str().unwrap()).unwrap();
assert_eq!(store.get("nonexistent").unwrap(), None);
}
#[test]
fn test_remove() {
let dir = tempdir().unwrap();
let mut store = KvStore::new(dir.path().to_str().unwrap()).unwrap();
store.set("key1".to_string(), "value1".to_string()).unwrap();
store.remove("key1").unwrap();
assert_eq!(store.get("key1").unwrap(), None);
}
#[test]
fn test_persistence() {
let dir = tempdir().unwrap();
let path = dir.path().to_str().unwrap();
{
let mut store = KvStore::new(path).unwrap();
store.set("key1".to_string(), "value1".to_string()).unwrap();
}
{
let store = KvStore::new(path).unwrap();
assert_eq!(store.get("key1").unwrap(), Some("value1".to_string()));
}
}
#[test]
fn test_overwrite() {
let dir = tempdir().unwrap();
let mut store = KvStore::new(dir.path().to_str().unwrap()).unwrap();
store.set("key1".to_string(), "value1".to_string()).unwrap();
store.set("key1".to_string(), "value2".to_string()).unwrap();
assert_eq!(store.get("key1").unwrap(), Some("value2".to_string()));
}
}