nostrdb-rs

nostrdb in rust!
git clone git://jb55.com/nostrdb-rs
Log | Files | Refs | Submodules | README | LICENSE

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:
M.gitignore | 2++
MCargo.toml | 5+++++
Msrc/config.rs | 10++++------
Msrc/error.rs | 4++++
Msrc/lib.rs | 12++++++++----
Msrc/ndb.rs | 51+++++++++++++++++++++++++++++++++++++++------------
Asrc/note.rs | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_util.rs | 6++++++
Asrc/transaction.rs | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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(); + } +}