1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
#[macro_use]
extern crate serde_derive;
extern crate recurdates;
extern crate bincode;
extern crate leveldb;
extern crate chrono;

use std::path::Path;

use leveldb::kv::KV;
use leveldb::iterator::Iterable;
use leveldb::database::{self, Database};
use leveldb::options::{Options, WriteOptions, ReadOptions};
use bincode::{serialize, deserialize, Infinite};

pub mod task;
use task::Task;

pub mod error;
use error::RustlerError;

#[cfg(test)]
mod tests;

pub struct Rustler {
    database: Database<i32>,
}

pub struct TaskIterator<'a> {
    iterator: database::iterator::Iterator<'a, i32>,
}

impl Rustler {
    /// Connect to the database at `path`. Create a file if `path` doesn't exist.
    pub fn open(path: &str) -> Rustler {
        let mut options = Options::new();
        options.create_if_missing = true;

        Rustler {
            database: Database::open(Path::new(path), options)
                .expect("Unable to open database."),
        }
    }

    /// Check if a task with the given id exists in the database.
    pub fn has(&self, tid: i32) -> bool {
        let r = self.database.get(ReadOptions::new(), tid);
        r.is_ok() && r.unwrap().is_some()
    }

    /// Returns a task with the given id. Error if none found.
    pub fn get(&self, tid: i32) -> Result<Task, RustlerError> {
        self.database.get(ReadOptions::new(), tid)
            .map_err(RustlerError::DBError)?
            .ok_or(RustlerError::NoTaskError(tid))
            .map(|x| deserialize::<Task>(&x).unwrap())
    }

    /// Deletes a task with the given id.
    /// Deleting a task deletes all its children.
    pub fn delete(&self, tid: i32) -> Result<(), RustlerError> {
        /* check if it exists */
        if self.has(tid) {
            self.database.delete(WriteOptions::new(), tid)
                .map_err(RustlerError::DBError)
        } else {
            Err(RustlerError::NoTaskError(tid))
        }
    }

    /// Inserts the given task. If there exists a task with the same id, it will be overwritten.
    /// Leave id as `None` for inserting a new task. The task's id will be populated after update.
    pub fn update(&self, t: &mut Task) -> Result<(), RustlerError> {
        let mut tmp = std::i32::MIN;
        /* assign new id */
        if t.id.is_none() {
            /* check for key collision */
            while self.has(tmp) {
                tmp += 1;
            }
            t.id = Some(tmp);
        }
        /* serialize & upsert */
        let st = serialize(&t, Infinite).unwrap();
        self.database.put(WriteOptions::new(), t.id.unwrap(), &st)
            .map_err(RustlerError::DBError)
    }

    /// Iterator visiting all tasks.
    pub fn iter(&self) -> TaskIterator {
        TaskIterator { iterator: self.database.iter(ReadOptions::new()) }
    }
}

impl<'a> Iterator for TaskIterator<'a> {
    type Item = Task;

    fn next(&mut self) -> Option<Self::Item> {
        self.iterator.next().map(|(_, v)| deserialize::<Task>(&v).unwrap())
    }
}