commit 5145c2d863848b7a3db92ea77de6e82d7e702fbb
parent fb992ce1dd5100eefae6b948d016915fbd2cdd0b
Author: William Casarin <jb55@jb55.com>
Date: Thu, 14 Dec 2023 23:26:34 -0800
rust: implement note, transaction apis
Diffstat:
9 files changed, 245 insertions(+), 22 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -2,3 +2,5 @@
/nostrdb
Cargo.lock
target/
+.build-result
+.buildcmd
diff --git a/Cargo.toml b/Cargo.toml
@@ -12,4 +12,9 @@ repository = "https://github.com/damus-io/nostrdb-rs/"
[build-dependencies]
cc = "1.0"
+
+[dependencies]
+env_logger = "0.10.1"
+libc = "0.2.151"
+log = "0.4.20"
#bindgen = "0.69.1" // re-enable when we update bindings
diff --git a/src/config.rs b/src/config.rs
@@ -1,12 +1,10 @@
use crate::bindings;
-// The Rust wrapper for ndb_config
-pub struct NdbConfig {
+pub struct Config {
pub config: bindings::ndb_config,
}
-impl NdbConfig {
- // Constructor
+impl Config {
pub fn new() -> Self {
let mut config = bindings::ndb_config {
filter_context: std::ptr::null_mut(),
@@ -20,10 +18,10 @@ impl NdbConfig {
bindings::ndb_default_config(&mut config);
}
- NdbConfig { config }
+ Config { config }
}
- // Example setter methods
+ //
pub fn set_flags(&mut self, flags: i32) -> &mut Self {
self.config.flags = flags;
self
diff --git a/src/error.rs b/src/error.rs
@@ -1,3 +1,7 @@
+#[derive(Debug, Eq, PartialEq)]
pub enum Error {
DbOpenFailed,
+ NotFound,
+ DecodeError,
+ TransactionFailed,
}
diff --git a/src/lib.rs b/src/lib.rs
@@ -4,7 +4,11 @@
#[allow(unused)]
mod bindings;
-mod config;
-mod error;
-mod ndb;
-mod result;
+pub mod config;
+pub mod error;
+pub mod ndb;
+pub mod note;
+pub mod result;
+pub mod transaction;
+
+mod test_util;
diff --git a/src/ndb.rs b/src/ndb.rs
@@ -2,9 +2,11 @@ use std::ffi::CString;
use std::ptr;
use crate::bindings;
-use crate::config::NdbConfig;
+use crate::config::Config;
use crate::error::Error;
+use crate::note::Note;
use crate::result::Result;
+use crate::transaction::Transaction;
pub struct Ndb {
ndb: *mut bindings::ndb,
@@ -12,7 +14,7 @@ pub struct Ndb {
impl Ndb {
// Constructor
- pub fn new(db_dir: &str, config: &NdbConfig) -> Result<Self> {
+ pub fn new(db_dir: &str, config: &Config) -> Result<Self> {
let db_dir_cstr = match CString::new(db_dir) {
Ok(cstr) => cstr,
Err(_) => return Err(Error::DbOpenFailed),
@@ -20,14 +22,42 @@ impl Ndb {
let mut ndb: *mut bindings::ndb = ptr::null_mut();
let result = unsafe { bindings::ndb_init(&mut ndb, db_dir_cstr.as_ptr(), config.as_ptr()) };
- if result != 0 {
+ if result == 0 {
return Err(Error::DbOpenFailed);
}
Ok(Ndb { ndb })
}
- // Add other methods to interact with the library here
+ pub fn get_note_by_id<'a>(
+ &self,
+ transaction: &'a mut Transaction,
+ id: &[u8; 32],
+ ) -> Result<Note<'a>> {
+ let mut len: usize = 0;
+ let mut primkey: u64 = 0;
+
+ let note_ptr = unsafe {
+ bindings::ndb_get_note_by_id(
+ transaction.as_mut_ptr(),
+ id.as_ptr(),
+ &mut len,
+ &mut primkey,
+ )
+ };
+
+ if note_ptr.is_null() {
+ // Handle null pointer (e.g., note not found or error occurred)
+ return Err(Error::NotFound);
+ }
+
+ // Convert the raw pointer to a Note instance
+ Ok(Note::new_transactional(note_ptr, len, primkey, transaction))
+ }
+
+ pub fn as_ptr(&self) -> *mut bindings::ndb {
+ return self.ndb;
+ }
}
impl Drop for Ndb {
@@ -41,21 +71,18 @@ impl Drop for Ndb {
#[cfg(test)]
mod tests {
use super::*;
+ use crate::config::Config;
+ use crate::test_util;
use std::fs;
- fn cleanup() {
- let _ = fs::remove_file("data.mdb");
- let _ = fs::remove_file("lock.mdb");
- }
-
#[test]
fn ndb_init_works() {
// Initialize ndb
{
- let cfg = NdbConfig::new();
- let _ = Ndb::new(".", &cfg);
+ let cfg = Config::new();
+ let _ = Ndb::new(".", &cfg).expect("ok");
}
- cleanup();
+ test_util::cleanup_db();
}
}
diff --git a/src/note.rs b/src/note.rs
@@ -0,0 +1,92 @@
+use crate::bindings;
+use crate::transaction::Transaction;
+
+#[derive(Debug)]
+pub enum Note<'a> {
+ Owned {
+ ptr: *mut bindings::ndb_note,
+ size: usize,
+ },
+
+ Transactional {
+ ptr: *mut bindings::ndb_note,
+ size: usize,
+ key: u64,
+ transaction: &'a Transaction,
+ },
+}
+
+impl<'a> Note<'a> {
+ pub fn new_owned(ptr: *mut bindings::ndb_note, size: usize) -> Note<'static> {
+ Note::Owned { ptr, size }
+ }
+
+ // Create a new note tied to a transaction
+ pub fn new_transactional(
+ ptr: *mut bindings::ndb_note,
+ size: usize,
+ key: u64,
+ transaction: &'a Transaction,
+ ) -> Note<'a> {
+ Note::Transactional {
+ ptr,
+ size,
+ key,
+ transaction,
+ }
+ }
+
+ pub fn size(&self) -> usize {
+ match self {
+ Note::Owned { size, .. } => *size,
+ Note::Transactional { size, .. } => *size,
+ }
+ }
+
+ pub fn as_ptr(&self) -> *mut bindings::ndb_note {
+ match self {
+ Note::Owned { ptr, .. } => *ptr,
+ Note::Transactional { ptr, .. } => *ptr,
+ }
+ }
+
+ pub fn kind(&self) -> u32 {
+ unsafe { bindings::ndb_note_kind(self.as_ptr()) }
+ }
+}
+
+impl<'a> Drop for Note<'a> {
+ fn drop(&mut self) {
+ match self {
+ Note::Owned { ptr, .. } => unsafe { libc::free((*ptr) as *mut libc::c_void) },
+ _ => (),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn note_query_works() {
+ use crate::config::Config;
+ use crate::error::Error;
+ use crate::ndb::Ndb;
+ use crate::test_util;
+
+ // Initialize ndb
+ {
+ let cfg = Config::new();
+ let ndb = Ndb::new(".", &cfg).expect("db open");
+ let mut txn = Transaction::new(&ndb).expect("new txn");
+
+ let err = ndb
+ .get_note_by_id(&mut txn, &[0; 32])
+ .expect_err("not found");
+ assert!(err == Error::NotFound);
+ }
+
+ test_util::cleanup_db();
+ }
+}
diff --git a/src/test_util.rs b/src/test_util.rs
@@ -0,0 +1,6 @@
+use std::fs;
+
+pub fn cleanup_db() {
+ let _ = fs::remove_file("data.mdb");
+ let _ = fs::remove_file("lock.mdb");
+}
diff --git a/src/transaction.rs b/src/transaction.rs
@@ -0,0 +1,85 @@
+use crate::bindings;
+use crate::error::Error;
+use crate::ndb::Ndb;
+use crate::result::Result;
+use log::debug;
+
+/// A `nostrdb` transaction. Only one is allowed to be active per thread.
+#[derive(Debug)]
+pub struct Transaction {
+ txn: bindings::ndb_txn,
+}
+
+impl Transaction {
+ /// Create a new `nostrdb` transaction. These are reference counted
+ pub fn new(ndb: &Ndb) -> Result<Self> {
+ // Initialize your transaction here
+ let mut txn = bindings::ndb_txn::new();
+ let res = unsafe { bindings::ndb_begin_query(ndb.as_ptr(), &mut txn) };
+
+ if res == 0 {
+ return Err(Error::TransactionFailed);
+ }
+
+ Ok(Transaction { txn })
+ }
+
+ pub fn as_ptr(&self) -> *const bindings::ndb_txn {
+ &self.txn
+ }
+
+ pub fn as_mut_ptr(&mut self) -> *mut bindings::ndb_txn {
+ &mut self.txn
+ }
+}
+
+impl Drop for Transaction {
+ fn drop(&mut self) {
+ // End the transaction
+ unsafe {
+ // Replace with your actual function
+ bindings::ndb_end_query(&mut self.txn);
+ }
+ }
+}
+
+impl bindings::ndb_txn {
+ fn new() -> Self {
+ // just create something uninitialized. ndb_begin_query will initialize it for us
+ let lmdb: *mut bindings::ndb_lmdb = std::ptr::null_mut();
+ let mdb_txn: *mut ::std::os::raw::c_void = std::ptr::null_mut();
+ bindings::ndb_txn {
+ lmdb: lmdb,
+ mdb_txn: mdb_txn,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::config::Config;
+ use crate::ndb::Ndb;
+ use crate::test_util;
+
+ #[test]
+ fn transaction_inheritence_fails() {
+ // Initialize ndb
+ {
+ let cfg = Config::new();
+ let ndb = Ndb::new(".", &cfg).expect("ndb open failed");
+
+ {
+ let txn = Transaction::new(&ndb).expect("txn1 failed");
+ let txn2 = Transaction::new(&ndb).expect_err("tx2");
+ assert!(txn2 == Error::TransactionFailed);
+ }
+
+ {
+ let txn = Transaction::new(&ndb).expect("txn1 failed");
+ }
+ }
+
+ test_util::cleanup_db();
+ }
+}