clementine 0.0.1

A tiny, embeddable, ACID compliant in-memory key/value database.
Documentation
use std::collections::*;
use std::ops::Deref;
use std::cell::RefCell;
use persist::Persistable;
use data::*;
use error::*;

#[derive(Debug)]
struct Item {
    key: String,
    value: Option<Data>,
}

impl Item {
    fn new(k: String, v: Option<Data>) -> Item {
        Item { key: k, value: v }
    }
}

pub trait ReadTransaction<K>
    where K: Into<String> + Ord + Clone
{
    fn get(&self, key: K) -> Option<&Data>;
    fn len(&self) -> usize;
    fn is_empty(&self) -> bool;
    fn contains_key(&self, key: K) -> bool;
}

pub trait WriteTransaction<K>: ReadTransaction<K>
    where K: Into<String> + Ord + Clone
{
    fn update(&mut self, key: K, value: Data) -> Option<Data>;
    fn remove(&mut self, key: K) -> Option<Data>;
    fn clear(&mut self);
}

pub struct Transaction {
    store: BTreeMap<String, Data>,
    persist_store: RefCell<Box<Persistable>>,
    backup_store: Option<BTreeMap<String, Data>>,
    items_to_sync: Vec<Item>,
    rollback_items: Vec<Item>,
}

impl Transaction {
    pub fn new(store: BTreeMap<String, Data>, persist: Box<Persistable>) -> Transaction {
        Transaction {
            store: store,
            backup_store: None,
            persist_store: RefCell::new(persist),
            items_to_sync: Vec::new(),
            rollback_items: Vec::new(),
        }
    }

    pub fn save(&self) -> Result<()> {
        for item in &self.items_to_sync {
            match item.value {
                Some(ref value) => {
                    self.persist_store
                        .borrow_mut()
                        .set(item.key.clone(), value.clone())?
                }
                None => self.persist_store.borrow_mut().remove(item.key.clone())?,
            }
        }

        Ok(())
    }

    pub fn commit(&mut self) {
        self.rollback_items.clear();
        self.backup_store = None;
    }

    pub fn rollback(&mut self) {
        if self.is_cleared() {
            self.store = self.backup_store.clone().unwrap();
            self.backup_store = None;
        }
        for item in &self.rollback_items {
            if item.value.is_none() {
                self.store.remove(&item.key.clone());
            } else {
                self.store
                    .insert(item.key.clone(), item.value.clone().unwrap());
            }
        }
    }

    fn record_rollback_item(&mut self, key: String, value: Option<Data>) {
        if self.backup_store.is_none() {
            self.rollback_items.push(Item::new(key, value));
        }
    }

    fn record_item_to_sync(&mut self, key: String, value: Option<Data>) {
        self.items_to_sync.push(Item::new(key, value));
    }

    fn is_cleared(&self) -> bool {
        self.backup_store.is_some()
    }
}

impl Deref for Transaction {
    type Target = BTreeMap<String, Data>;
    fn deref(&self) -> &BTreeMap<String, Data> {
        &self.store
    }
}

impl<K> ReadTransaction<K> for Transaction
    where K: Into<String> + Ord + Clone
{
    fn get(&self, key: K) -> Option<&Data> {
        self.store.get(&key.into())
    }

    fn len(&self) -> usize {
        self.store.len()
    }

    fn is_empty(&self) -> bool {
        self.store.is_empty()
    }

    fn contains_key(&self, key: K) -> bool {
        self.store.contains_key(&key.into())
    }
}

impl<K> WriteTransaction<K> for Transaction
    where K: Into<String> + Ord + Clone
{
    fn update(&mut self, key: K, value: Data) -> Option<Data> {
        let previous_value = self.store.insert(key.clone().into(), value.clone());
        self.record_rollback_item(key.clone().into(), previous_value.clone());
        self.record_item_to_sync(key.into(), Some(value));

        previous_value
    }

    fn remove(&mut self, key: K) -> Option<Data> {
        let previous_value = self.store.remove(&key.clone().into());
        self.record_rollback_item(key.clone().into(), previous_value.clone());
        self.record_item_to_sync(key.into(), None);

        previous_value
    }

    fn clear(&mut self) {
        self.backup_store = Some(self.store.clone());
        self.store.clear();
    }
}