1use std::collections::HashMap;
2use std::fs;
3use std::path::Path;
4use bincode::{serialize, deserialize};
5use std::io::{Error, ErrorKind};
6
7#[derive(Debug)]
8pub struct KvStore {
9 keys: HashMap<String, String>,
10 storage_path: String,
11}
12
13impl KvStore {
14 pub fn new(storage_path: &str) -> Result<KvStore, Error> {
15 fs::create_dir_all(storage_path)?;
16 Ok(KvStore {
17 keys: HashMap::new(),
18 storage_path: storage_path.to_string(),
19 })
20 }
21
22 pub fn set(&mut self, key: String, value: String) -> Result<(), Error> {
23 let file_path = format!("{}/{}.bin", self.storage_path, key);
24 let encoded: Vec<u8> = serialize(&value).map_err(|e| Error::new(ErrorKind::Other, e))?;
25 fs::write(file_path, encoded)?;
26 self.keys.insert(key, value);
27 Ok(())
28 }
29
30 pub fn get(&self, key: &str) -> Result<Option<String>, Error> {
31 if let Some(value) = self.keys.get(key) {
32 return Ok(Some(value.clone()));
33 }
34
35 let file_path = format!("{}/{}.bin", self.storage_path, key);
36 if !Path::new(&file_path).exists() {
37 return Ok(None);
38 }
39
40 let data = fs::read(file_path)?;
41 let decoded: String = deserialize(&data).map_err(|e| Error::new(ErrorKind::Other, e))?;
42 Ok(Some(decoded))
43 }
44
45 pub fn remove(&mut self, key: &str) -> Result<(), Error> {
46 let file_path = format!("{}/{}.bin", self.storage_path, key);
47 if Path::new(&file_path).exists() {
48 fs::remove_file(file_path)?;
49 }
50 self.keys.remove(key);
51 Ok(())
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use tempfile::tempdir;
59
60 #[test]
61 fn test_set_get() {
62 let dir = tempdir().unwrap();
63 let mut store = KvStore::new(dir.path().to_str().unwrap()).unwrap();
64
65 store.set("key1".to_string(), "value1".to_string()).unwrap();
66 assert_eq!(store.get("key1").unwrap(), Some("value1".to_string()));
67 }
68
69 #[test]
70 fn test_get_non_existent() {
71 let dir = tempdir().unwrap();
72 let store = KvStore::new(dir.path().to_str().unwrap()).unwrap();
73
74 assert_eq!(store.get("nonexistent").unwrap(), None);
75 }
76
77 #[test]
78 fn test_remove() {
79 let dir = tempdir().unwrap();
80 let mut store = KvStore::new(dir.path().to_str().unwrap()).unwrap();
81
82 store.set("key1".to_string(), "value1".to_string()).unwrap();
83 store.remove("key1").unwrap();
84 assert_eq!(store.get("key1").unwrap(), None);
85 }
86
87 #[test]
88 fn test_persistence() {
89 let dir = tempdir().unwrap();
90 let path = dir.path().to_str().unwrap();
91
92 {
93 let mut store = KvStore::new(path).unwrap();
94 store.set("key1".to_string(), "value1".to_string()).unwrap();
95 }
96
97 {
98 let store = KvStore::new(path).unwrap();
99 assert_eq!(store.get("key1").unwrap(), Some("value1".to_string()));
100 }
101 }
102
103 #[test]
104 fn test_overwrite() {
105 let dir = tempdir().unwrap();
106 let mut store = KvStore::new(dir.path().to_str().unwrap()).unwrap();
107
108 store.set("key1".to_string(), "value1".to_string()).unwrap();
109 store.set("key1".to_string(), "value2".to_string()).unwrap();
110 assert_eq!(store.get("key1").unwrap(), Some("value2".to_string()));
111 }
112}