#![deny(missing_docs)]
extern crate bincode;
#[macro_use]
extern crate log;
extern crate persy;
extern crate serde;
#[macro_use]
extern crate serde_derive;
pub mod macros;
use std::collections::HashMap;
use std::hash::Hash;
use bincode::{deserialize, serialize};
use serde::de::Deserialize;
use serde::ser::Serialize;
use persy::{Config, PRes, Persy, PersyError};
#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
pub enum Value {
String(String),
Int(i32),
Float(f32),
Map(HashMap<String, Value>),
List(Vec<Value>),
}
type KVResult = PRes<bool>;
type KVError = PersyError;
const SEGMENT_NAME: &str = "tdb";
pub struct KV<K, V> {
cab: HashMap<K, V>,
persy: Persy,
id: Option<persy::PersyId>,
}
impl<K, V> KV<K, V>
where
K: Clone + Serialize + for<'kde> Deserialize<'kde> + Eq + Hash,
V: Clone + Serialize + for<'vde> Deserialize<'vde>,
{
pub fn new(p: &'static str) -> Result<KV<K, V>, PersyError> {
match Persy::create(p) {
Ok(o) => o,
Err(PersyError::IO(ref e)) if e == "File exists (os error 17)" => (),
Err(e) => return Err(e),
}
let persy = Persy::open(p, Config::new())?;
if !persy.exists_segment(SEGMENT_NAME)? {
let mut tx = persy.begin()?;
persy.create_segment(&mut tx, SEGMENT_NAME)?;
let prepared = persy.prepare_commit(tx)?;
persy.commit(prepared)?;
}
let mut store = KV {
cab: HashMap::new(),
persy,
id: None,
};
store.load_from_persist()?;
Ok(store)
}
pub fn insert(&mut self, key: K, value: V) -> KVResult {
self.cab.insert(key, value);
self.write_to_persist()
}
pub fn get(&mut self, key: &K) -> Result<Option<V>, KVError> {
Ok(self.cab.get(&key).map(|v| (*v).clone()))
}
pub fn remove(&mut self, key: &K) -> KVResult {
self.cab.remove(&key);
self.write_to_persist()
}
pub fn keys(&mut self) -> Result<Vec<K>, KVError> {
Ok(self.cab.keys().cloned().collect())
}
fn write_to_persist(&mut self) -> KVResult {
let mut tx = self.persy.begin()?;
let byte_vec: Vec<u8> = match serialize(&self.cab) {
Ok(bv) => bv,
Err(e) => {
error!("serialize: {}", e);
return Err(PersyError::Err("Couldn't encode".to_string()));
}
};
match &self.id {
Some(ref x) => self.persy.update_record(&mut tx, SEGMENT_NAME, x, &byte_vec)?,
None => self.id = Some(self.persy.insert_record(&mut tx, SEGMENT_NAME, &byte_vec)?),
}
let prepared = self.persy.prepare_commit(tx)?;
self.persy.commit(prepared)?;
Ok(true)
}
fn load_from_persist(&mut self) -> KVResult {
if self.persy.exists_segment(SEGMENT_NAME)? {
let deserbv = |byte_vec: &Vec<u8>| {
match deserialize(byte_vec.as_slice()) {
Ok(f) => {
Ok(f)
}
Err(e) => {
error!("{}", e);
Err(PersyError::Err("Couldn't decode cab".to_string()))
}
}
};
if let Some(ref x) = &self.id {
if let Some(byte_vec) = self.persy.read_record(SEGMENT_NAME, x)? {
self.cab = deserbv(&byte_vec)?;
return Ok(true);
}
}
for (id, byte_vec) in self.persy.scan(SEGMENT_NAME)? {
self.cab = deserbv(&byte_vec)?;
self.id = Some(id);
}
}
Ok(true)
}
}