fs_db/
lib.rs

1
2use std::{fs, io};
3use std::path::{Path, PathBuf};
4use std::marker::PhantomData;
5use std::fmt::Debug;
6
7extern crate serde;
8use serde::{de::DeserializeOwned, Serialize};
9
10extern crate serde_json;
11
12/// A simple file system based key:value data store
13pub struct FileStore<V> {
14    dir: PathBuf,
15    _v: PhantomData<V>,
16}
17
18#[derive(Debug)]
19pub enum Error<E> {
20    Io(io::Error),
21    Inner(E),
22}
23
24impl <E> From<io::Error> for Error<E> {
25    fn from(e: io::Error) -> Self {
26        Error::Io(e)
27    }
28}
29
30
31impl <V, E>FileStore<V> 
32where
33    V: EncodeDecode<Value=V, Error=E> + Serialize + DeserializeOwned + Debug,
34    E: Debug
35{
36    /// Create a new FileStore
37    pub fn new<P: AsRef<Path>>(dir: P) -> Result<Self, Error<E>> {
38        Ok(FileStore{
39            dir: dir.as_ref().into(), 
40            _v: PhantomData
41        })
42    }
43
44    /// List all files in the database
45    pub fn list(&mut self) -> Result<Vec<String>, Error<E>> {
46        let mut names = vec![];
47
48        for entry in fs::read_dir(&self.dir)? {
49            let entry = entry?;
50            let name = entry.file_name().into_string().unwrap();
51            names.push(name);
52        }
53
54        Ok(names)
55    }
56
57    /// Load a file by name
58    pub fn load<P: AsRef<Path>>(&mut self, name: P) -> Result<V, Error<E>> {
59        let mut path = self.dir.clone();
60        path.push(name);
61
62        let buff = fs::read(path)?;
63        let obj: V = V::decode(&buff).map_err(|e| Error::Inner(e) )?;
64
65        Ok(obj)
66    }
67
68    /// Store a file by name
69    pub fn store<P: AsRef<Path>>(&mut self, name: P, v: &V) -> Result<(), Error<E>> {
70        let mut path = self.dir.clone();
71        path.push(name);
72        
73        let bin: Vec<u8> = V::encode(v).map_err(|e| Error::Inner(e) )?;
74        fs::write(path, bin)?;
75        Ok(())
76    }
77
78    /// Load all files from the database
79    pub fn load_all(&mut self) -> Result<Vec<(String, V)>, Error<E>> {
80        let mut objs = vec![];
81
82        for entry in fs::read_dir(&self.dir)? {
83            let entry = entry?;
84            let name = entry.file_name().into_string().unwrap();
85
86            let buff = fs::read(entry.path())?;
87            let obj: V = V::decode(&buff).map_err(|e| Error::Inner(e) )?;
88
89            objs.push((name, obj));
90        }
91
92        Ok(objs)
93    }
94
95    /// Store a colection of files in the database
96    pub fn store_all(&mut self, data: &[(String, V)]) -> Result<(), Error<E>> {
97        for (name, value) in data {
98            self.store(name, value)?;
99        }
100
101        Ok(())
102    }
103
104
105    /// Remove a file from the database
106    pub fn rm<P: AsRef<Path>>(&mut self, name: P) -> Result<(), Error<E>> {
107        let mut path = self.dir.clone();
108        path.push(name);
109
110        fs::remove_file(path)?;
111
112        Ok(())
113    }
114
115}
116
117/// EncodeDecode trait must be implemented for FileStore types
118pub trait EncodeDecode {
119    type Value;
120    type Error;
121
122    fn encode(value: &Self::Value) -> Result<Vec<u8>, Self::Error>;
123    fn decode(buff: &[u8]) -> Result<Self::Value, Self::Error>;
124}
125
126/// Automagic EncodeDecode implementation for serde capable types
127impl <V> EncodeDecode for V
128where
129    V: Serialize + DeserializeOwned + Debug,
130{
131    type Value = V;
132    type Error = serde_json::Error;
133
134    fn encode(value: &Self::Value) -> Result<Vec<u8>, Self::Error> {
135        serde_json::to_vec(value)
136    }
137
138    fn decode(buff: &[u8]) -> Result<Self::Value, Self::Error> {
139        serde_json::from_slice(&buff)
140    }
141}
142
143
144#[cfg(test)]
145mod tests {
146    use std::env;
147
148    use super::*;
149
150    const N: usize = 3;
151
152    #[test]
153    fn mock_database() {
154
155        let dir = env::temp_dir();
156
157        let mut s = FileStore::new(dir).unwrap();
158
159        for i in 0..N {
160            let name = format!("{}", i);
161
162            s.store(&name, &i).unwrap();
163
164            let v = s.load(&name).unwrap();
165
166            assert_eq!(i, v);
167        }
168    }
169}