use anyhow::Result;
use rmp_serde::Serializer;
use serde::de::DeserializeOwned;
#[allow(unused_imports)]
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs::{create_dir_all, metadata, read, read_dir, remove_dir, remove_file, write};
pub struct PackDb<T: DeserializeOwned + Serialize> {
store: String,
#[allow(dead_code)]
a: Option<T>,
}
impl<T: DeserializeOwned + Serialize> PackDb<T> {
pub fn new(store: Option<String>) -> Self {
let loc = store.unwrap_or_else(|| "data".into());
create_dir_all(&loc).unwrap();
PackDb::<T> {
store: loc,
a: None,
}
}
fn path<K: std::fmt::Display>(&self, key: K) -> String {
format!("{}/{}.pak", &self.store, key)
}
pub fn has<K: std::fmt::Display>(&self, key: K) -> bool {
let exists = metadata(self.path(key));
exists.is_ok()
}
pub fn set<K: std::fmt::Display>(&self, key: K, val: T) -> Result<()> {
let mut buf = vec![];
val.serialize(&mut Serializer::new(&mut buf))?;
write(self.path(key), buf)?;
Ok(())
}
pub fn get<K: std::fmt::Display>(&self, key: K) -> Result<T> {
let r = read(self.path(key))?;
Ok(rmp_serde::from_read_ref(&r)?)
}
pub fn get_all(&self) -> Result<HashMap<String, T>> {
let mut res = HashMap::new();
let entries = read_dir(&self.store)?;
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().replace(".pak", "");
res.insert(name.clone(), self.get(&name)?);
}
Ok(res)
}
pub fn keys(&self) -> Result<Vec<String>> {
let mut res = vec![];
let entries = read_dir(&self.store)?;
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().replace(".pak", "");
res.push(name);
}
Ok(res)
}
pub fn len(&self) -> Result<usize> {
let entries = read_dir(&self.store)?;
Ok(entries.flatten().count())
}
pub fn is_empty(&self) -> Result<bool> {
Ok(self.len()? == 0)
}
pub fn insert_many<K: std::fmt::Display>(&self, data: HashMap<K, T>) -> Result<bool> {
for (key, val) in data {
self.set(key, val)?;
}
Ok(true)
}
pub fn delete<K: std::fmt::Display>(&self, key: K) -> bool {
let exists = remove_file(self.path(key));
exists.is_ok()
}
pub fn clear(&self) -> Result<bool> {
remove_dir(&self.store)?;
create_dir_all(&self.store)?;
Ok(true)
}
pub fn drain(&self) -> Result<HashMap<String, T>> {
let data = self.get_all();
self.clear()?;
data
}
}
#[cfg(test)]
mod test {
#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct Human {
age: u32,
name: String,
}
use super::*;
#[test]
fn test() -> Result<()> {
let storage = PackDb::<Human>::new(Some("store".to_owned()));
storage.set(
"testing",
Human {
age: 22,
name: "this is a test".into(),
},
)?;
let user = storage.get("testing")?;
assert_eq!(user.name, "this is a test".to_owned());
let users = storage.get_all()?;
let _user = users.get("testing").unwrap();
assert_eq!(storage.has("testing"), true);
assert_eq!(storage.delete("testing"), true);
assert_eq!(storage.has("testing"), false);
Ok(())
}
}