Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial LmdbAdapter #1

Merged
merged 1 commit into from Jun 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
@@ -1,2 +1,3 @@
target
Cargo.lock
*.rs.bk
3 changes: 2 additions & 1 deletion Cargo.toml
Expand Up @@ -10,4 +10,5 @@ version = "0.0.0"
authors = ["Tony Arcieri <bascule@gmail.com>"]

[dependencies]
lmdb-rs = ">= 0.7.1"
lmdb = ">= 0.5.0"
lmdb-sys = ">= 0.5.0"
172 changes: 168 additions & 4 deletions src/lib.rs
@@ -1,6 +1,170 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
extern crate lmdb;
extern crate lmdb_sys;

use std::{result, str};
use std::path::Path;
use lmdb::{Transaction, Cursor, DUP_SORT, INTEGER_KEY};

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum Error {
DbCreateError,
DbOpenError,
DbWriteError,
TransactionError,
}

pub type Result<T> = result::Result<T, Error>;

struct LmdbAdapter {
env: lmdb::Environment,
path2id: lmdb::Database,
id2entry: lmdb::Database,
}

struct RwTransaction<'a> {
lmdb_txn: lmdb::RwTransaction<'a>,
}

struct Node<'a> {
id: u64,
parent_id: u64,
name: &'a str,
objectclass: &'a str,
}

impl LmdbAdapter {
pub fn create(path: &Path) -> Result<LmdbAdapter> {
let env = match lmdb::Environment::new()
.set_max_dbs(8)
.open_with_permissions(&path, 0o600) {
Ok(e) => e,
Err(_) => return Err(Error::DbCreateError),
};

let path2id = match env.create_db(Some("path2id"), INTEGER_KEY | DUP_SORT) {
Ok(db) => db,
Err(_) => return Err(Error::DbCreateError),
};

let id2entry = match env.create_db(Some("id2entry"), INTEGER_KEY) {
Ok(db) => db,
Err(_) => return Err(Error::DbCreateError),
};

Ok(LmdbAdapter {
env: env,
path2id: path2id,
id2entry: id2entry,
})
}

pub fn open(path: &Path) -> Result<LmdbAdapter> {
let env = match lmdb::Environment::new().open(&path) {
Ok(e) => e,
Err(_) => return Err(Error::DbOpenError),
};

let path2id = match env.open_db(Some("path2id")) {
Ok(e) => e,
Err(_) => return Err(Error::DbOpenError),
};

let id2entry = match env.open_db(Some("id2entry")) {
Ok(e) => e,
Err(_) => return Err(Error::DbOpenError),
};

Ok(LmdbAdapter {
env: env,
path2id: path2id,
id2entry: id2entry,
})
}

fn id2bytes(id: u64) -> [u8; 8] {
unsafe { std::mem::transmute(id) }
}

fn bytes2id(bytes: [u8; 8]) -> u64 {
unsafe { std::mem::transmute(bytes) }
}

pub fn rw_transaction(&self) -> Result<RwTransaction> {
match self.env.begin_rw_txn() {
Ok(txn) => Ok(RwTransaction { lmdb_txn: txn }),
Err(_) => Err(Error::TransactionError),
}
}

pub fn next_available_id(&self, txn: &mut RwTransaction) -> Result<u64> {
let cursor = match txn.lmdb_txn.open_rw_cursor(self.path2id) {
Ok(c) => c,
Err(_) => return Err(Error::TransactionError),
};

let last_id = match cursor.get(None, None, lmdb_sys::MDB_LAST) {
Ok((key, _)) => {
let mut key_bytes = [0u8; 8];
key_bytes.copy_from_slice(key.unwrap()); // TODO: don't panic
LmdbAdapter::bytes2id(key_bytes)
}
Err(_) => 0, // 0 is reserved for root
};

Ok(last_id + 1)
}

pub fn add_node<'a>(&'a self,
txn: &mut RwTransaction,
id: u64,
parent_id: u64,
name: &'a str,
objectclass: &'a str)
-> Result<Node> {
let ref mut lmdb_txn = txn.lmdb_txn;
let id_bytes = LmdbAdapter::id2bytes(id);

let mut path_info = Vec::with_capacity(8 + name.len());
path_info.extend_from_slice(&id_bytes);
path_info.extend_from_slice(name.as_bytes());

match lmdb_txn.put(self.path2id,
&LmdbAdapter::id2bytes(parent_id),
&path_info,
lmdb::WriteFlags::empty()) {
Ok(_) => (),
Err(_) => return Err(Error::DbWriteError),
}

match lmdb_txn.put(self.id2entry,
&id_bytes,
&objectclass,
lmdb::WriteFlags::empty()) {
Ok(_) => (),
Err(_) => return Err(Error::DbWriteError),
}

Ok(Node {
id: id,
parent_id: parent_id,
name: name,
objectclass: objectclass,
})
}
}

impl<'a> RwTransaction<'a> {
pub fn commit(self) -> () {
self.lmdb_txn.commit().unwrap()
}
}

#[test]
fn test_stuff() {
let path = Path::new("./tmp");
let adapter = LmdbAdapter::create(path).unwrap();
let mut txn = adapter.rw_transaction().unwrap();
let id = adapter.next_available_id(&mut txn).unwrap();
let node = adapter.add_node(&mut txn, id, 0, "example.com", "domain").unwrap();
txn.commit();
}
1 change: 1 addition & 0 deletions tmp/.gitignore
@@ -0,0 +1 @@
*