neveko/neveko-core/src/db.rs
2023-06-03 11:22:40 -04:00

158 lines
5.2 KiB
Rust

// db created and exported from here
extern crate lmdb_rs as lmdb;
use lmdb::{
DbFlags,
DbHandle,
EnvBuilder,
Environment,
};
use log::{
debug,
error,
};
use crate::utils;
/// LMDB Interface allows access to the env
///
/// and handle for the write, read and delete
///
/// functionality.
pub struct Interface {
pub env: Environment,
pub handle: DbHandle,
}
impl Interface {
/// Instantiation of ```Environment``` and ```DbHandle```
pub fn open() -> Self {
let release_env = utils::get_release_env();
let file_path = format!(
"/home/{}/.{}/",
std::env::var("USER").unwrap_or(String::from("user")),
crate::APP_NAME,
);
let mut env_str: &str = "test-lmdb";
if release_env != utils::ReleaseEnvironment::Development {
env_str = "lmdb";
};
let env = EnvBuilder::new()
.open(format!("{}/{}", file_path, env_str), 0o777)
.expect(&format!("could not open LMDB at {}", file_path));
let handle = env.get_default_db(DbFlags::empty()).unwrap();
Interface { env, handle }
}
pub async fn async_open() -> Self {
tokio::time::sleep(std::time::Duration::from_micros(1)).await;
self::Interface::open()
}
/// Write a key-value to LMDB. NEVEKO does not currently support
///
/// writing multiple key value pairs.
pub fn write(e: &Environment, h: &DbHandle, k: &str, v: &str) {
let txn = e.new_transaction().unwrap();
{
// get a database bound to this transaction
let db = txn.bind(&h);
let pair = vec![(k, v)];
for &(key, value) in pair.iter() {
db.set(&key, &value).unwrap();
}
}
match txn.commit() {
Err(_) => error!("failed to commit!"),
Ok(_) => (),
}
}
pub async fn async_write(e: &Environment, h: &DbHandle, k: &str, v: &str) {
tokio::time::sleep(std::time::Duration::from_micros(1)).await;
self::Interface::write(e, h, k, v)
}
/// Read a value from LMDB by passing the key as a static
///
/// string. If the value does not exist an empty string is
///
/// returned. NEVEKO does not currently support duplicate keys.
pub fn read(e: &Environment, h: &DbHandle, k: &str) -> String {
let reader = e.get_reader().unwrap();
let db = reader.bind(&h);
let value = db.get::<&str>(&k).unwrap_or_else(|_| "");
let r = String::from(value);
{
if r == utils::empty_string() {
debug!("Failed to read from db.")
}
}
r
}
pub async fn async_read(e: &Environment, h: &DbHandle, k: &str) -> String {
tokio::time::sleep(std::time::Duration::from_micros(1)).await;
self::Interface::read(e, h, k)
}
/// Delete a value from LMDB by passing the key as a
///
/// static string. If the value does not exist then an
///
/// error will be logged.
pub fn delete(e: &Environment, h: &DbHandle, k: &str) {
let txn = e.new_transaction().unwrap();
{
// get a database bound to this transaction
let db = txn.bind(&h);
db.del(&k).unwrap_or_else(|_| error!("failed to delete"));
}
match txn.commit() {
Err(_) => error!("failed to commit!"),
Ok(_) => (),
}
}
pub async fn async_delete(e: &Environment, h: &DbHandle, k: &str) {
tokio::time::sleep(std::time::Duration::from_micros(1)).await;
self::Interface::delete(e, h, k)
}
}
// Tests
//-------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn async_write_and_read_test() {
// run and async cleanup so the test doesn't fail when deleting test data
use tokio::runtime::Runtime;
let rt = Runtime::new().expect("Unable to create Runtime for test");
let _enter = rt.enter();
tokio::spawn(async move {
let s = Interface::async_open().await;
let k = "async-test-key";
let v = "async-test-value";
Interface::async_write(&s.env, &s.handle, k, v).await;
let expected = String::from(v);
let actual = Interface::async_read(&s.env, &s.handle, k).await;
assert_eq!(expected, actual);
Interface::async_delete(&s.env, &s.handle, &k).await;
});
}
#[test]
fn async_write_and_delete_test() {
// run and async cleanup so the test doesn't fail when deleting test data
use tokio::runtime::Runtime;
let rt = Runtime::new().expect("Unable to create Runtime for test");
let _enter = rt.enter();
tokio::spawn(async move {
let s = Interface::open();
let k = "write_and_delete_test_test-key";
let v = "write_and_delete_test_test-value";
Interface::async_write(&s.env, &s.handle, k, v).await;
let expected = utils::empty_string();
Interface::async_delete(&s.env, &s.handle, &k).await;
let actual = Interface::async_read(&s.env, &s.handle, k).await;
assert_eq!(expected, actual);
});
}
}