use std::ffi::CString;
use std::path::Path;
use std::ptr;
use std::sync::Arc;
use libdb_sys::ffi as db_ffi;
use super::dbt::DBT;
use super::error;
use super::error::Error;
#[cfg(all(not(feature = "v5_3"), not(feature = "v4_8")))] use super::flags_5_3::Flags;
#[cfg(feature = "v5_3")] use super::flags_5_3::Flags;
#[cfg(feature = "v4_8")] use super::flags_4_8::Flags;
pub type Environment = Arc<Env>;
pub type Database = Arc<Db>;
pub struct EnvironmentBuilder {
env_ptr: *mut db_ffi::DB_ENV,
home: Option<CString>,
flags: Flags,
mode: i32,
}
impl EnvironmentBuilder {
pub fn new() -> EnvironmentBuilder {
unsafe {
let mut env_ptr: *mut db_ffi::DB_ENV = ptr::null_mut();
let ret = db_ffi::db_env_create(&mut env_ptr, 0);
match ret {
0 => EnvironmentBuilder {
env_ptr: env_ptr,
home: None,
flags: Flags::DB_NONE,
mode: 0,
},
e => panic!("Could not instantiate DB_ENV: {}", e)
}
}
}
pub fn home<P: AsRef<Path>>(mut self, home: P) -> Self {
self.home = Some(CString::new(home.as_ref().to_str().unwrap()).unwrap());
self
}
pub fn flags(mut self, flags: Flags) -> Self {
self.flags = flags;
self
}
pub fn mode(mut self, mode: i32) -> Self {
self.mode = mode;
self
}
pub fn open(mut self) -> Result<Environment, Error> {
let home_ptr = match self.home.as_ref() {
Some(cstr) => cstr.as_ptr(),
None => ptr::null()
};
unsafe {
match ((*self.env_ptr).open.unwrap())(self.env_ptr, home_ptr, self.flags.bits(), self.mode) {
0 => {
let env = Env {
env_ptr: self.env_ptr,
};
self.env_ptr = ptr::null_mut();
Ok(Arc::new(env))
},
e => Err(Error::new(e)),
}
}
}
}
impl Drop for EnvironmentBuilder {
fn drop(&mut self) {
if ptr::null() != self.env_ptr {
unsafe {
((*self.env_ptr).close.unwrap())(self.env_ptr, 0);
}
}
}
}
pub struct Env {
env_ptr: *mut db_ffi::DB_ENV,
}
impl Env {
pub fn txn(&self, parent: Option<&Transaction>, flags: Flags) -> Result<Transaction, Error> {
unsafe {
let mut txn_ptr: *mut db_ffi::DB_TXN = ptr::null_mut();
let ret = ((*self.env_ptr).txn_begin.unwrap())(self.env_ptr, unwrap_txn_ptr(parent), &mut txn_ptr, flags.bits());
match ret {
0 => Ok(Transaction { txn_ptr: txn_ptr }),
e => Err(Error::new(e)),
}
}
}
}
impl Drop for Env {
fn drop(&mut self) {
if ptr::null() != self.env_ptr {
unsafe {
((*self.env_ptr).close.unwrap())(self.env_ptr, 0);
}
}
}
}
pub enum DbType {
BTree,
Hash,
Recno,
Queue,
Any,
}
impl From<DbType> for db_ffi::DBTYPE {
fn from(flavor: DbType) -> Self {
match flavor {
DbType::BTree => db_ffi::DBTYPE_DB_BTREE,
DbType::Hash => db_ffi::DBTYPE_DB_HASH,
DbType::Recno => db_ffi::DBTYPE_DB_RECNO,
DbType::Queue => db_ffi::DBTYPE_DB_QUEUE,
DbType::Any => db_ffi::DBTYPE_DB_UNKNOWN,
}
}
}
pub struct DatabaseBuilder<'a> {
env: Option<Environment>,
txn: Option<&'a Transaction>,
file: Option<CString>,
name: Option<CString>,
flags: Flags,
mode: i32,
db_type: DbType,
}
impl<'a> DatabaseBuilder<'a> {
pub fn new() -> DatabaseBuilder<'a> {
DatabaseBuilder {
env: None,
txn: None,
file: None,
name: None,
flags: Flags::DB_NONE,
mode: 0,
db_type: DbType::BTree,
}
}
pub fn environment(mut self, env: &Environment) -> Self {
self.env = Some(env.clone());
self
}
pub fn transaction(mut self, txn: &'a Transaction) -> Self {
self.txn = Some(txn);
self
}
pub fn file<P: AsRef<Path>>(mut self, file: P) -> Self {
self.file = Some(CString::new(file.as_ref().to_str().unwrap()).unwrap());
self
}
pub fn name(mut self, name: &str) -> Self {
self.name = Some(CString::new(name).unwrap());
self
}
pub fn flags(mut self, flags: Flags) -> Self {
self.flags = flags;
self
}
pub fn mode(mut self, mode: i32) -> Self {
self.mode = mode;
self
}
pub fn db_type(mut self, db_type: DbType) -> Self {
self.db_type = db_type;
self
}
pub fn open(self) -> Result<Database, Error> {
let env_ptr = match self.env.as_ref() {
Some(env) => env.env_ptr,
None => ptr::null_mut()
};
let file_ptr = match self.file.as_ref() {
Some(cstr) => cstr.as_ptr(),
None => ptr::null()
};
let database_ptr = match self.name.as_ref() {
Some(cstr) => cstr.as_ptr(),
None => ptr::null()
};
let dbtype = db_ffi::DBTYPE::from(self.db_type);
unsafe {
let mut db: *mut db_ffi::DB = ptr::null_mut();
let ret = db_ffi::db_create(&mut db, env_ptr, 0);
if ret != 0 {
panic!("Could not instantiate DB. errno = {}", ret);
}
let ret = ((*db).open.unwrap())(db, unwrap_txn_ptr(self.txn), file_ptr, database_ptr, dbtype, self.flags.bits(), self.mode);
match ret {
0 => Ok(Arc::new(Db { env: self.env, db: db })),
e => {
((*db).close.unwrap())(db, 0);
Err(Error::new(e))
},
}
}
}
}
pub struct Db {
env: Option<Environment>,
db: *mut db_ffi::DB,
}
impl Db {
pub fn get(&self, txn: Option<&Transaction>, key: &mut [u8], flags: Flags) -> Result<Option<DBT>, Error> {
let mut key_dbt: db_ffi::DBT = Default::default();
key_dbt.data = key.as_mut_ptr() as *mut ::std::os::raw::c_void;
key_dbt.size = key.len() as u32;
let mut data_dbt: db_ffi::DBT = Default::default();
data_dbt.flags = db_ffi::DB_DBT_MALLOC;
unsafe {
match ((*self.db).get.unwrap())(self.db, unwrap_txn_ptr(txn), &mut key_dbt, &mut data_dbt, flags.bits()) {
0 => Ok(Some(DBT::from(data_dbt))),
error::DB_NOTFOUND => Ok(None),
e => Err(Error::new(e))
}
}
}
pub fn put(&self, txn: Option<&Transaction>, key: &mut [u8], data: &mut [u8], flags: Flags) -> Result<(), Error> {
let mut key_dbt: db_ffi::DBT = Default::default();
key_dbt.data = key.as_mut_ptr() as *mut ::std::os::raw::c_void;
key_dbt.size = key.len() as u32;
let mut data_dbt: db_ffi::DBT = Default::default();
data_dbt.data = data.as_mut_ptr() as *mut ::std::os::raw::c_void;
data_dbt.size = data.len() as u32;
unsafe {
match ((*self.db).put.unwrap())(self.db, unwrap_txn_ptr(txn), &mut key_dbt, &mut data_dbt, flags.bits()) {
0 => Ok(()),
e => Err(Error::new(e))
}
}
}
pub fn cursor(&self) -> Result<Cursor, Error> {
let mut dbc: db_ffi::DBC = db_ffi::DBC::default();
let mut dbc_ptr: *mut db_ffi::DBC = &mut dbc as *mut db_ffi::DBC;
unsafe {
match ((*self.db).cursor.unwrap())(self.db, ptr::null_mut(), &mut dbc_ptr as *mut *mut db_ffi::DBC, 0) {
0 => Ok(Cursor{dbc_ptr}),
e => Err(Error::new(e)),
}
}
}
}
pub struct Cursor {
dbc_ptr: *mut db_ffi::DBC,
}
impl Cursor {
pub fn next(&mut self) -> Result<(Option<DBT>, Option<DBT>), Error> {
let mut key_dbt: db_ffi::DBT = Default::default();
key_dbt.flags = db_ffi::DB_DBT_MALLOC;
let mut data_dbt: db_ffi::DBT = Default::default();
data_dbt.flags = db_ffi::DB_DBT_MALLOC;
unsafe {
match ((*self.dbc_ptr).c_get.unwrap())(self.dbc_ptr, &mut key_dbt, &mut data_dbt, db_ffi::DB_NEXT) {
0 => Ok((Some(DBT::from(key_dbt)), Some(DBT::from(data_dbt)))),
e => Err(Error::new(e)),
}
}
}
}
impl Drop for Db {
fn drop(&mut self) {
unsafe {
((*self.db).close.unwrap())(self.db, 0);
}
}
}
pub struct Transaction {
txn_ptr: *mut db_ffi::DB_TXN,
}
#[repr(u32)]
pub enum CommitType {
Inherit = 0,
NoSync = db_ffi::DB_TXN_NOSYNC,
Sync = db_ffi::DB_TXN_SYNC,
}
impl Transaction {
pub fn commit(mut self, mode: CommitType) -> Result<(), Error> {
unsafe {
let ret = match ((*self.txn_ptr).commit.unwrap())(self.txn_ptr, mode as u32) {
0 => Ok(()),
e => Err(Error::new(e))
};
self.txn_ptr = ptr::null_mut();
ret
}
}
pub fn abort(mut self) -> Result<(), Error> {
unsafe {
let ret = match ((*self.txn_ptr).abort.unwrap())(self.txn_ptr) {
0 => Ok(()),
e => Err(Error::new(e))
};
self.txn_ptr = ptr::null_mut();
ret
}
}
}
impl Drop for Transaction {
fn drop(&mut self) {
if ptr::null() != self.txn_ptr {
unsafe {
((*self.txn_ptr).abort.unwrap())(self.txn_ptr);
}
}
}
}
fn unwrap_txn_ptr(txn: Option<&Transaction>) -> *mut db_ffi::DB_TXN {
match txn {
Some(txn) => txn.txn_ptr,
None => ptr::null_mut()
}
}
unsafe impl Send for Env {}
unsafe impl Sync for Env {}
unsafe impl Send for Db {}
unsafe impl Sync for Db {}