1use kivis::Repository;
2use std::{fs, path::PathBuf};
3
4use crate::error::FileStoreError;
5
6#[derive(Debug)]
11pub struct FileStore {
12 data_dir: PathBuf,
14}
15
16impl Repository for FileStore {
17 type K = String;
18 type V = String;
19 type Error = FileStoreError;
20
21 fn insert_entry(&mut self, key: &str, value: &str) -> Result<(), Self::Error> {
22 let file_path = self.key_to_filename(key);
23 fs::write(file_path, value)?;
24 Ok(())
25 }
26
27 fn get_entry(&self, key: &str) -> Result<Option<Self::V>, Self::Error> {
28 let file_path = self.key_to_filename(key);
29 match fs::read_to_string(file_path) {
30 Ok(data) => Ok(Some(data)),
31 Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
32 Err(e) => Err(e.into()),
33 }
34 }
35
36 fn remove_entry(&mut self, key: &str) -> Result<Option<Self::V>, Self::Error> {
37 let file_path = self.key_to_filename(key);
38 match fs::read_to_string(&file_path) {
39 Ok(data) => {
40 fs::remove_file(file_path)?;
41 Ok(Some(data))
42 }
43 Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
44 Err(e) => Err(e.into()),
45 }
46 }
47
48 fn scan_range(
49 &self,
50 range: std::ops::Range<Self::K>,
51 ) -> Result<impl Iterator<Item = Result<Self::K, Self::Error>>, Self::Error> {
52 let entries = fs::read_dir(&self.data_dir)?;
53
54 let mut keys: Vec<String> = Vec::new();
55 for entry in entries.flatten() {
56 if let Some(filename) = entry.file_name().to_str()
57 && let Some(key) = self.filename_to_key(filename)
58 && key >= range.start
59 && key < range.end
60 {
61 keys.push(key);
62 }
63 }
64
65 keys.sort();
66 Ok(keys.into_iter().rev().map(Ok))
67 }
68}
69
70impl FileStore {
71 pub fn new(data_dir: impl Into<PathBuf>) -> std::io::Result<Self> {
79 let data_dir = data_dir.into();
80 fs::create_dir_all(&data_dir)?;
81 Ok(Self { data_dir })
82 }
83
84 fn key_to_filename(&self, key: &str) -> PathBuf {
86 self.data_dir.join(format!("{key}.dat"))
87 }
88
89 fn filename_to_key(&self, filename: &str) -> Option<String> {
91 filename.strip_suffix(".dat").map(String::from)
92 }
93}