Skip to content

Commit

Permalink
Add note blocks iterator
Browse files Browse the repository at this point in the history
  • Loading branch information
jb55 committed Dec 31, 2023
1 parent 05ae967 commit eebb544
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.direnv/
/nostrdb
Cargo.lock
tags
target/
.build-result
.buildcmd
254 changes: 254 additions & 0 deletions src/block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
use crate::{bindings, Note, Transaction};

#[derive(Debug)]
pub struct Blocks<'a> {
ptr: *mut bindings::ndb_blocks,
txn: Option<&'a Transaction>,
}

#[derive(Debug)]
pub struct Block<'a> {
ptr: *mut bindings::ndb_block,
txn: Option<&'a Transaction>,
}

pub struct BlockIter<'a> {
iter: bindings::ndb_block_iterator,
txn: Option<&'a Transaction>,
}

#[derive(Debug, Eq, PartialEq)]
enum BlockType {
Hashtag,
Text,
MentionIndex,
MentionBech32,
Url,
Invoice,
}

impl<'a> Block<'a> {
pub(crate) fn new_transactional(
ptr: *mut bindings::ndb_block,
txn: &'a Transaction,
) -> Block<'a> {
Block {
ptr,
txn: Some(txn),
}
}

pub(crate) fn new_owned(ptr: *mut bindings::ndb_block) -> Block<'static> {
Block { ptr, txn: None }
}

pub(crate) fn new(ptr: *mut bindings::ndb_block, txn: Option<&'a Transaction>) -> Block<'a> {
Block { ptr, txn }
}

pub fn as_ptr(&self) -> *mut bindings::ndb_block {
self.ptr
}

pub fn as_str(&self) -> &'a str {
unsafe {
let str_block = bindings::ndb_block_str(self.as_ptr());
if str_block.is_null() {
return "";
}
let ptr = bindings::ndb_str_block_ptr(str_block) as *const u8;
let len = bindings::ndb_str_block_len(str_block);
let byte_slice = std::slice::from_raw_parts(ptr, len.try_into().unwrap());
std::str::from_utf8_unchecked(byte_slice)
}
}

pub fn blocktype(&self) -> BlockType {
let typ = unsafe { bindings::ndb_get_block_type(self.as_ptr()) };
println!("type {}", typ);
let r = match typ {
1 => BlockType::Hashtag,
2 => BlockType::Text,
3 => BlockType::MentionIndex,
4 => BlockType::MentionBech32,
5 => BlockType::Url,
6 => BlockType::Invoice,
_ => panic!("Invalid blocktype {}", typ),
};
println!("typer {:?}", r);
r
}
}

impl<'a> Blocks<'a> {
pub(crate) fn new_transactional(
ptr: *mut bindings::ndb_blocks,
txn: &'a Transaction,
) -> Blocks<'a> {
Blocks {
ptr,
txn: Some(txn),
}
}

pub(crate) fn new_owned(ptr: *mut bindings::ndb_blocks) -> Blocks<'static> {
Blocks { ptr, txn: None }
}

pub fn iter(&self, note: &Note<'a>) -> BlockIter<'a> {
let content = note.content_ptr();
match self.txn {
Some(txn) => BlockIter::new_transactional(content, self.as_ptr(), txn),
None => BlockIter::new_owned(content, self.as_ptr()),
}
}

pub fn as_ptr(&self) -> *mut bindings::ndb_blocks {
self.ptr
}
}

impl<'a> BlockIter<'a> {
pub(crate) fn new_transactional(
content: *const ::std::os::raw::c_char,
blocks: *mut bindings::ndb_blocks,
txn: &'a Transaction,
) -> BlockIter<'a> {
let type_ = bindings::ndb_block_type_BLOCK_TEXT;
let mention_index: u32 = 1;
let block = bindings::ndb_block__bindgen_ty_1 { mention_index };
let block = bindings::ndb_block { type_, block };
let p = blocks as *mut ::std::os::raw::c_uchar;
let iter = bindings::ndb_block_iterator {
content,
blocks,
p,
block,
};
let mut block_iter = BlockIter {
iter,
txn: Some(txn),
};
unsafe { bindings::ndb_blocks_iterate_start(content, blocks, &mut block_iter.iter) };
block_iter
}

pub(crate) fn new_owned(
content: *const ::std::os::raw::c_char,
blocks: *mut bindings::ndb_blocks,
) -> BlockIter<'static> {
let type_ = bindings::ndb_block_type_BLOCK_TEXT;
let mention_index: u32 = 1;
let block = bindings::ndb_block__bindgen_ty_1 { mention_index };
let block = bindings::ndb_block { type_, block };
let p = blocks as *mut ::std::os::raw::c_uchar;
let mut iter = bindings::ndb_block_iterator {
content,
blocks,
p,
block,
};
unsafe { bindings::ndb_blocks_iterate_start(content, blocks, &mut iter) };
BlockIter { iter, txn: None }
}

pub fn as_ptr(&self) -> *const bindings::ndb_block_iterator {
&self.iter
}

pub fn as_mut_ptr(&self) -> *mut bindings::ndb_block_iterator {
self.as_ptr() as *mut bindings::ndb_block_iterator
}
}

impl<'a> Iterator for BlockIter<'a> {
type Item = Block<'a>;

fn next(&mut self) -> Option<Self::Item> {
let block = unsafe { bindings::ndb_blocks_iterate_next(self.as_mut_ptr()) };
if block.is_null() {
return None;
}

Some(Block::new(block, self.txn))
}
}

/*
impl<'a> IntoIterator for Blocks<'a> {
type Item = Block<'a>;
type IntoIter = BlockIter<'a>;
fn into_iter(self) -> Self::IntoIter {
match self.txn {
Some(txn) => BlockIter::new_transactional(self.as_ptr(), txn),
None => BlockIter::new_owned(self.as_ptr()),
}
}
}
*/

#[cfg(test)]
mod tests {
use super::*;
use crate::test_util;
use crate::{Config, Ndb};

#[test]
fn note_blocks_work() {
let db = "target/testdbs/note_blocks";

{
let mut ndb = Ndb::new(db, &Config::new()).expect("ndb");
ndb.process_event("[\"EVENT\",\"s\",{\"id\":\"d28ac02e277c3cf2744b562a414fd92d5fea554a737901364735bfe74577f304\",\"pubkey\":\"b5b1b5d2914daa2eda99af22ae828effe98730bf69dcca000fa37bfb9e395e32\",\"created_at\": 1703989205,\"kind\": 1,\"tags\": [],\"content\": \"#hashtags, are neat nostr:nprofile1qqsr9cvzwc652r4m83d86ykplrnm9dg5gwdvzzn8ameanlvut35wy3gpz3mhxue69uhhyetvv9ujuerpd46hxtnfduyu75sw https://github.com/damus-io\",\"sig\": \"07af3062616a17ef392769cadb170ac855c817c103e007c72374499bbadb2fe8917a0cc5b3fdc5aa5d56de086e128b3aeaa8868f6fe42a409767241b6a29cc94\"}]").expect("process ok");
}

{
let ndb = Ndb::new(db, &Config::new()).expect("ndb");
let id =
hex::decode("d28ac02e277c3cf2744b562a414fd92d5fea554a737901364735bfe74577f304")
.expect("hex id");
let txn = Transaction::new(&ndb).expect("txn");
let id_bytes: [u8; 32] = id.try_into().expect("id bytes");
let note = ndb.get_note_by_id(&txn, &id_bytes).unwrap();
let blocks = ndb
.get_blocks_by_key(&txn, note.key().unwrap())
.expect("note");
let mut c = 0;
for block in blocks.iter(&note) {
match c {
0 => {
assert_eq!(block.blocktype(), BlockType::Hashtag);
assert_eq!(block.as_str(), "hashtags");
}

1 => {
assert_eq!(block.blocktype(), BlockType::Text);
assert_eq!(block.as_str(), ", are neat ");
}

2 => {
assert_eq!(block.blocktype(), BlockType::MentionBech32);
assert_eq!(block.as_str(), "nprofile1qqsr9cvzwc652r4m83d86ykplrnm9dg5gwdvzzn8ameanlvut35wy3gpz3mhxue69uhhyetvv9ujuerpd46hxtnfduyu75sw");
}

3 => {
assert_eq!(block.blocktype(), BlockType::Text);
assert_eq!(block.as_str(), " ");
}

4 => {
assert_eq!(block.blocktype(), BlockType::Url);
assert_eq!(block.as_str(), "https://github.com/damus-io");
}

_ => assert!(false),
}

c += 1;
}
}

test_util::cleanup_db(&db);
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod bindings;
#[allow(non_snake_case)]
mod ndb_profile;

mod block;
mod config;
mod error;
mod ndb;
Expand All @@ -16,6 +17,7 @@ mod profile;
mod result;
mod transaction;

pub use block::Blocks;
pub use config::Config;
pub use error::Error;
pub use ndb::Ndb;
Expand Down
30 changes: 28 additions & 2 deletions src/ndb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::ffi::CString;
use std::ptr;

use crate::bindings;
use crate::{Config, Error, Note, ProfileRecord, Result, Transaction};
use crate::{Blocks, Config, Error, Note, ProfileRecord, Result, Transaction};
use std::fs;
use std::path::Path;
use std::sync::Arc;
Expand Down Expand Up @@ -88,7 +88,7 @@ impl Ndb {

let profile_record_ptr = unsafe {
bindings::ndb_get_profile_by_pubkey(
transaction.as_ptr() as *mut bindings::ndb_txn,
transaction.as_mut_ptr(),
id.as_ptr(),
&mut len,
&mut primkey,
Expand All @@ -109,6 +109,32 @@ impl Ndb {
))
}

pub fn get_notekey_by_id(&self, txn: &Transaction, id: &[u8; 32]) -> Result<u64> {
let res = unsafe {
bindings::ndb_get_notekey_by_id(
txn.as_mut_ptr(),
id.as_ptr() as *const ::std::os::raw::c_uchar,
)
};

if res == 0 {
return Err(Error::NotFound);
}

Ok(res)
}

pub fn get_blocks_by_key<'a>(&self, txn: &'a Transaction, note_key: u64) -> Result<Blocks<'a>> {
let blocks_ptr =
unsafe { bindings::ndb_get_blocks_by_key(self.as_ptr(), txn.as_mut_ptr(), note_key) };

if blocks_ptr.is_null() {
return Err(Error::NotFound);
}

Ok(Blocks::new_transactional(blocks_ptr, txn))
}

/// Get a note from the database. Takes a [Transaction] and a 32-byte [Note] Id
pub fn get_note_by_id<'a>(
&self,
Expand Down
13 changes: 12 additions & 1 deletion src/note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ impl<'a> Note<'a> {
}
}

pub fn key(&self) -> Option<u64> {
match self {
Note::Transactional { key, .. } => Some(*key),
_ => None,
}
}

pub fn size(&self) -> usize {
match self {
Note::Owned { size, .. } => *size,
Expand All @@ -71,10 +78,14 @@ impl<'a> Note<'a> {
unsafe { bindings::ndb_note_content_length(self.as_ptr()) as usize }
}

pub fn content_ptr(&self) -> *const ::std::os::raw::c_char {
unsafe { bindings::ndb_note_content(self.as_ptr()) }
}

/// Get the [`Note`] contents.
pub fn content(&self) -> &'a str {
unsafe {
let content = bindings::ndb_note_content(self.as_ptr());
let content = self.content_ptr();
let byte_slice = std::slice::from_raw_parts(content as *const u8, self.content_size());
std::str::from_utf8_unchecked(byte_slice)
}
Expand Down
4 changes: 2 additions & 2 deletions src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ impl Transaction {
&self.txn
}

pub fn as_mut_ptr(&mut self) -> *mut bindings::ndb_txn {
&mut self.txn
pub fn as_mut_ptr(&self) -> *mut bindings::ndb_txn {
self.as_ptr() as *mut bindings::ndb_txn
}
}

Expand Down

0 comments on commit eebb544

Please sign in to comment.