networking/
database.rs

1use crate::asyncronous::encryption::{
2    database_aes_decrypt as sym_aes_decrypt, database_aes_encrypt as sym_aes_encrypt,
3};
4use crate::error::NetworkError;
5use serde::{de::DeserializeOwned, Serialize};
6use std::collections::hash_map::{IntoIter, Iter, IterMut};
7use std::collections::HashMap;
8use std::fmt;
9use std::iter::IntoIterator;
10use std::path::Path;
11use std::path::PathBuf;
12
13use std::fs::File;
14use std::io::{Read, Write};
15use std::{fmt::Debug, hash::Hash};
16use tar::{Archive, Builder};
17use walkdir::WalkDir;
18
19/// this is marker trait for any value of HashDatabase
20pub trait HashValue: 'static + Debug + Serialize + DeserializeOwned + Clone + Send + Sync {}
21/// this is a marker trait for any key value of HashDatabase
22pub trait HashKey: 'static + Hash + ToString + PartialEq + Eq + Clone + Send + Sync {}
23impl<V> HashValue for V where V: 'static + Debug + Serialize + DeserializeOwned + Send + Clone + Sync
24{}
25impl<K> HashKey for K where K: 'static + ToString + Hash + PartialEq + Eq + Clone + Send + Sync {}
26impl<V: HashValue, K: HashKey> Debug for HashDatabase<V, K>
27where
28    K: Debug,
29    V: Debug,
30{
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        f.debug_struct("Point")
33            .field("data", &self.data)
34            .field("key", &self.key)
35            .field("root", &self.root)
36            .finish()
37    }
38}
39/// these trait implementations are taken directly from std::collections::HashMap
40/// note that currently these implementations will only return data already loaded into memory
41impl<'a, V: HashValue, K: HashKey> IntoIterator for &'a HashDatabase<V, K> {
42    type Item = (&'a K, &'a V);
43    type IntoIter = Iter<'a, K, V>;
44
45    #[inline]
46    fn into_iter(self) -> Iter<'a, K, V> {
47        self.data.iter()
48    }
49}
50impl<V: HashValue, K: HashKey> IntoIterator for HashDatabase<V, K> {
51    type Item = (K, V);
52    type IntoIter = IntoIter<K, V>;
53    fn into_iter(self) -> IntoIter<K, V> {
54        self.data.into_iter()
55    }
56}
57impl<'a, V: HashValue, K: HashKey> IntoIterator for &'a mut HashDatabase<V, K> {
58    type Item = (&'a K, &'a mut V);
59    type IntoIter = IterMut<'a, K, V>;
60
61    #[inline]
62    fn into_iter(self) -> IterMut<'a, K, V> {
63        self.data.iter_mut()
64    }
65}
66#[derive(Clone)]
67pub struct HashDatabase<V: HashValue, K: HashKey = String> {
68    data: HashMap<K, V>,
69    key: Vec<u8>,
70    root: PathBuf,
71}
72impl<V: HashValue, K: HashKey> HashDatabase<V, K> {
73    /// # Arguments
74    ///
75    /// path: path to the root of the database
76    /// key: an option of bytes used to encrypt and decrypt the database
77    pub fn new<P: AsRef<Path>>(path: P, key: Vec<u8>) -> Result<Self, NetworkError> {
78        let root = path.as_ref().to_path_buf();
79        if !root.exists() {
80            std::fs::create_dir(root.clone())?;
81        }
82        Ok(Self {
83            data: HashMap::new(),
84            key,
85            root,
86        })
87    }
88    pub fn insert(&mut self, key: K, item: V) -> Result<(), NetworkError> {
89        self.data.insert(key.clone(), item.clone());
90        let path_str = key.to_string();
91        let path = self.root.join(path_str);
92        let mut file = File::create(path)?;
93        let mut data = serde_json::to_string(&item)?.into_bytes();
94        sym_aes_encrypt(&self.key, &mut data);
95        Ok(file.write_all(&data)?)
96    }
97    pub fn get(&self, key: &K) -> Option<&V> {
98        self.data.get(key)
99    }
100    pub fn load(&mut self, key: &K) -> Result<(), NetworkError> {
101        let path_str = key.to_string();
102        let path = self.root.join(path_str);
103        if !path.exists() {
104            return Err(NetworkError::IOError(std::io::Error::new(
105                std::io::ErrorKind::NotFound,
106                "entry not found",
107            )));
108        }
109        let mut file = File::open(path)?;
110        let mut invec = Vec::new();
111        file.read_to_end(&mut invec)?;
112        sym_aes_decrypt(&self.key, &mut invec);
113        let entry = serde_json::from_str(&String::from_utf8(invec)?)?;
114        self.data.insert(key.clone(), entry);
115        Ok(())
116    }
117    pub fn decompose(self) -> (HashMap<K, V>, PathBuf) {
118        (self.data, self.root)
119    }
120    /// indexes all files in the root
121    /// note that this function is highly inefficient, as such it should only be called when absolutely needed
122    pub fn index_entries(&self) -> Result<Vec<PathBuf>, NetworkError> {
123        let results: Vec<Result<PathBuf, NetworkError>> = WalkDir::new(self.root.clone())
124            .into_iter()
125            .map(|p| Ok(p?.path().to_path_buf()))
126            .collect();
127        let mut paths = Vec::with_capacity(results.len());
128        for result in results.into_iter() {
129            let path = result?;
130            if path.is_file() {
131                paths.push(path);
132            }
133        }
134        Ok(paths)
135    }
136    /// indexes all directories in the root
137    pub fn index_subdatabases(&self) -> Result<Vec<PathBuf>, NetworkError> {
138        let results: Vec<Result<PathBuf, NetworkError>> = WalkDir::new(self.root.clone())
139            .into_iter()
140            .map(|p| Ok(p?.path().to_path_buf()))
141            .collect();
142        let mut paths = Vec::with_capacity(results.len());
143        for result in results.into_iter() {
144            let path = result?;
145            if path.is_dir() {
146                paths.push(path);
147            }
148        }
149        Ok(paths)
150    }
151    /// reads entry of a different type from the database
152    /// used for special exceptions, for any large quantity of this type create a different database
153    pub fn read_entry<EV: HashValue>(&self, key: &K) -> Result<EV, NetworkError> {
154        let path_str = key.to_string();
155        let path = self.root.join(path_str);
156        let mut file = File::open(path)?;
157        let mut invec = Vec::new();
158        file.read_to_end(&mut invec)?;
159        sym_aes_decrypt(&self.key, &mut invec);
160        Ok(serde_json::from_str(&String::from_utf8(invec)?)?)
161    }
162    /// writes entry of different type from that of the database
163    /// used for special exceptions, for any large quantity of this type create a different database
164    pub fn write_entry<EV: HashValue>(&self, key: &K, value: &EV) -> Result<(), NetworkError> {
165        let path_str = key.to_string();
166        let path = self.root.join(path_str);
167        let mut file = File::open(path)?;
168        let mut outvec = serde_json::to_string(value)?.into_bytes();
169        sym_aes_encrypt(&self.key, &mut outvec);
170        Ok(file.write_all(&outvec)?)
171    }
172    /// converts the database to an in memory tar file
173    pub fn archive(&self) -> Result<Vec<u8>, NetworkError> {
174        let archive = Vec::new();
175        let mut builder = Builder::new(archive);
176        let results: Vec<std::io::Result<()>> = WalkDir::new(self.root.clone())
177            .into_iter()
178            .map(|p| p.unwrap().into_path())
179            .filter(|p| p.is_file())
180            .map(|p| builder.append_path(p))
181            .collect();
182        for result in results.into_iter() {
183            result?
184        }
185        Ok(builder.into_inner()?)
186    }
187    /// takes an in memory tar file and writes it to the disk
188    pub fn dearchive(&self, archive_data: &[u8]) -> std::io::Result<()> {
189        let mut archive = Archive::new(archive_data);
190        for entry in archive.entries()? {
191            let mut entry = entry?;
192            entry.unpack(self.root.join(entry.path()?))?;
193        }
194        Ok(())
195    }
196}