block.rs (11629B)
1 use crate::{bindings, Note, Transaction}; 2 3 #[derive(Debug)] 4 pub struct Blocks<'a> { 5 ptr: *mut bindings::ndb_blocks, 6 txn: Option<&'a Transaction>, 7 } 8 9 #[derive(Debug)] 10 pub struct Block<'a> { 11 ptr: *mut bindings::ndb_block, 12 13 #[allow(dead_code)] 14 txn: Option<&'a Transaction>, 15 } 16 17 pub struct BlockIter<'a> { 18 iter: bindings::ndb_block_iterator, 19 txn: Option<&'a Transaction>, 20 } 21 22 #[derive(Debug, Eq, PartialEq)] 23 pub enum BlockType { 24 Hashtag, 25 Text, 26 MentionIndex, 27 MentionBech32, 28 Url, 29 Invoice, 30 } 31 32 #[derive(Debug, Eq, PartialEq)] 33 pub enum Bech32Type { 34 Event, 35 Pubkey, 36 Profile, 37 Note, 38 Relay, 39 Addr, 40 Secret, 41 } 42 43 pub enum Mention<'a> { 44 Pubkey(&'a bindings::bech32_npub), 45 Event(&'a bindings::bech32_nevent), 46 Profile(&'a bindings::bech32_nprofile), 47 Note(&'a bindings::bech32_note), 48 Relay(&'a bindings::bech32_nrelay), 49 Secret(&'a bindings::bech32_nsec), 50 Addr(&'a bindings::bech32_naddr), 51 } 52 53 impl bindings::ndb_str_block { 54 pub fn as_str(&self) -> &str { 55 unsafe { 56 let ptr = bindings::ndb_str_block_ptr(self as *const Self as *mut Self) as *const u8; 57 let len = bindings::ndb_str_block_len(self as *const Self as *mut Self); 58 let byte_slice = std::slice::from_raw_parts(ptr, len.try_into().unwrap()); 59 std::str::from_utf8_unchecked(byte_slice) 60 } 61 } 62 } 63 64 impl bindings::bech32_nrelay { 65 pub fn as_str(&self) -> &str { 66 self.relay.as_str() 67 } 68 } 69 70 impl bindings::bech32_nprofile { 71 pub fn pubkey(&self) -> &[u8; 32] { 72 unsafe { &*(self.pubkey as *const [u8; 32]) } 73 } 74 } 75 76 impl bindings::bech32_npub { 77 pub fn pubkey(&self) -> &[u8; 32] { 78 unsafe { &*(self.pubkey as *const [u8; 32]) } 79 } 80 } 81 82 impl bindings::bech32_note { 83 pub fn id(&self) -> &[u8; 32] { 84 unsafe { &*(self.event_id as *const [u8; 32]) } 85 } 86 } 87 88 impl bindings::bech32_nevent { 89 pub fn id(&self) -> &[u8; 32] { 90 unsafe { &*(self.event_id as *const [u8; 32]) } 91 } 92 93 pub fn pubkey(&self) -> Option<&[u8; 32]> { 94 unsafe { 95 if self.pubkey.is_null() { 96 return None; 97 } 98 Some(&*(self.pubkey as *const [u8; 32])) 99 } 100 } 101 } 102 103 impl<'a> Mention<'a> { 104 pub fn new(bech32: &'a bindings::nostr_bech32) -> Self { 105 unsafe { 106 match Bech32Type::from_ctype(bech32.type_) { 107 Bech32Type::Event => Mention::Event(&bech32.__bindgen_anon_1.nevent), 108 Bech32Type::Pubkey => Mention::Pubkey(&bech32.__bindgen_anon_1.npub), 109 Bech32Type::Profile => Mention::Profile(&bech32.__bindgen_anon_1.nprofile), 110 Bech32Type::Note => Mention::Note(&bech32.__bindgen_anon_1.note), 111 Bech32Type::Relay => Mention::Relay(&bech32.__bindgen_anon_1.nrelay), 112 Bech32Type::Secret => Mention::Secret(&bech32.__bindgen_anon_1.nsec), 113 Bech32Type::Addr => Mention::Addr(&bech32.__bindgen_anon_1.naddr), 114 } 115 } 116 } 117 } 118 119 impl Bech32Type { 120 pub(crate) fn from_ctype(typ: bindings::nostr_bech32_type) -> Bech32Type { 121 match typ { 122 1 => Bech32Type::Note, 123 2 => Bech32Type::Pubkey, 124 3 => Bech32Type::Profile, 125 4 => Bech32Type::Event, 126 5 => Bech32Type::Relay, 127 6 => Bech32Type::Addr, 128 7 => Bech32Type::Secret, 129 _ => panic!("Invalid bech32 type"), 130 } 131 } 132 } 133 134 impl<'a> Block<'a> { 135 #[allow(dead_code)] 136 pub(crate) fn new_transactional( 137 ptr: *mut bindings::ndb_block, 138 txn: &'a Transaction, 139 ) -> Block<'a> { 140 Block { 141 ptr, 142 txn: Some(txn), 143 } 144 } 145 146 #[allow(dead_code)] 147 pub(crate) fn new_owned(ptr: *mut bindings::ndb_block) -> Block<'static> { 148 Block { ptr, txn: None } 149 } 150 151 pub(crate) fn new(ptr: *mut bindings::ndb_block, txn: Option<&'a Transaction>) -> Block<'a> { 152 Block { ptr, txn } 153 } 154 155 pub fn as_ptr(&self) -> *mut bindings::ndb_block { 156 self.ptr 157 } 158 159 pub fn as_mention(&self) -> Option<Mention<'a>> { 160 if self.blocktype() != BlockType::MentionBech32 { 161 return None; 162 } 163 Some(Mention::new(self.c_bech32())) 164 } 165 166 pub fn as_str(&self) -> &'a str { 167 unsafe { 168 let str_block = bindings::ndb_block_str(self.as_ptr()); 169 if str_block.is_null() { 170 return ""; 171 } 172 173 (*str_block).as_str() 174 } 175 } 176 177 fn c_bech32(&self) -> &'a bindings::nostr_bech32 { 178 unsafe { &(*self.as_ptr()).block.mention_bech32.bech32 } 179 } 180 181 pub fn blocktype(&self) -> BlockType { 182 let typ = unsafe { bindings::ndb_get_block_type(self.as_ptr()) }; 183 match typ { 184 1 => BlockType::Hashtag, 185 2 => BlockType::Text, 186 3 => BlockType::MentionIndex, 187 4 => BlockType::MentionBech32, 188 5 => BlockType::Url, 189 6 => BlockType::Invoice, 190 _ => panic!("Invalid blocktype {}", typ), 191 } 192 } 193 } 194 195 impl<'a> Blocks<'a> { 196 pub(crate) fn new_transactional( 197 ptr: *mut bindings::ndb_blocks, 198 txn: &'a Transaction, 199 ) -> Blocks<'a> { 200 Blocks { 201 ptr, 202 txn: Some(txn), 203 } 204 } 205 206 #[allow(dead_code)] 207 pub(crate) fn new_owned(ptr: *mut bindings::ndb_blocks) -> Blocks<'static> { 208 Blocks { ptr, txn: None } 209 } 210 211 pub fn iter(&self, note: &Note<'a>) -> BlockIter<'a> { 212 let content = note.content_ptr(); 213 match self.txn { 214 Some(txn) => BlockIter::new_transactional(content, self.as_ptr(), txn), 215 None => BlockIter::new_owned(content, self.as_ptr()), 216 } 217 } 218 219 pub fn as_ptr(&self) -> *mut bindings::ndb_blocks { 220 self.ptr 221 } 222 } 223 224 impl Drop for Blocks<'_> { 225 fn drop(&mut self) { 226 unsafe { bindings::ndb_blocks_free(self.as_ptr()) }; 227 } 228 } 229 230 impl<'a> BlockIter<'a> { 231 pub(crate) fn new_transactional( 232 content: *const ::std::os::raw::c_char, 233 blocks: *mut bindings::ndb_blocks, 234 txn: &'a Transaction, 235 ) -> BlockIter<'a> { 236 let type_ = bindings::ndb_block_type_BLOCK_TEXT; 237 let mention_index: u32 = 1; 238 let block = bindings::ndb_block__bindgen_ty_1 { mention_index }; 239 let block = bindings::ndb_block { type_, block }; 240 let p = blocks as *mut ::std::os::raw::c_uchar; 241 let iter = bindings::ndb_block_iterator { 242 content, 243 blocks, 244 p, 245 block, 246 }; 247 let mut block_iter = BlockIter { 248 iter, 249 txn: Some(txn), 250 }; 251 unsafe { bindings::ndb_blocks_iterate_start(content, blocks, &mut block_iter.iter) }; 252 block_iter 253 } 254 255 pub(crate) fn new_owned( 256 content: *const ::std::os::raw::c_char, 257 blocks: *mut bindings::ndb_blocks, 258 ) -> BlockIter<'static> { 259 let type_ = bindings::ndb_block_type_BLOCK_TEXT; 260 let mention_index: u32 = 1; 261 let block = bindings::ndb_block__bindgen_ty_1 { mention_index }; 262 let block = bindings::ndb_block { type_, block }; 263 let p = blocks as *mut ::std::os::raw::c_uchar; 264 let mut iter = bindings::ndb_block_iterator { 265 content, 266 blocks, 267 p, 268 block, 269 }; 270 unsafe { bindings::ndb_blocks_iterate_start(content, blocks, &mut iter) }; 271 BlockIter { iter, txn: None } 272 } 273 274 pub fn as_ptr(&self) -> *const bindings::ndb_block_iterator { 275 &self.iter 276 } 277 278 pub fn as_mut_ptr(&self) -> *mut bindings::ndb_block_iterator { 279 self.as_ptr() as *mut bindings::ndb_block_iterator 280 } 281 } 282 283 impl<'a> Iterator for BlockIter<'a> { 284 type Item = Block<'a>; 285 286 fn next(&mut self) -> Option<Self::Item> { 287 let block = unsafe { bindings::ndb_blocks_iterate_next(self.as_mut_ptr()) }; 288 if block.is_null() { 289 return None; 290 } 291 292 Some(Block::new(block, self.txn)) 293 } 294 } 295 296 /* 297 impl<'a> IntoIterator for Blocks<'a> { 298 type Item = Block<'a>; 299 type IntoIter = BlockIter<'a>; 300 301 fn into_iter(self) -> Self::IntoIter { 302 match self.txn { 303 Some(txn) => BlockIter::new_transactional(self.as_ptr(), txn), 304 None => BlockIter::new_owned(self.as_ptr()), 305 } 306 } 307 } 308 */ 309 310 #[cfg(test)] 311 mod tests { 312 use super::*; 313 use crate::test_util; 314 use crate::{Config, Ndb}; 315 316 #[test] 317 fn note_blocks_work() { 318 let db = "target/testdbs/note_blocks"; 319 test_util::cleanup_db(&db); 320 321 { 322 let ndb = Ndb::new(db, &Config::new()).expect("ndb"); 323 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"); 324 } 325 326 { 327 let ndb = Ndb::new(db, &Config::new()).expect("ndb"); 328 let id = 329 hex::decode("d28ac02e277c3cf2744b562a414fd92d5fea554a737901364735bfe74577f304") 330 .expect("hex id"); 331 let pubkey = 332 hex::decode("32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245") 333 .expect("jb55 pubkey"); 334 let txn = Transaction::new(&ndb).expect("txn"); 335 let id_bytes: [u8; 32] = id.try_into().expect("id bytes"); 336 let pubkey_bytes: [u8; 32] = pubkey.try_into().expect("pubkey bytes"); 337 let note = ndb.get_note_by_id(&txn, &id_bytes).unwrap(); 338 let blocks = ndb 339 .get_blocks_by_key(&txn, note.key().unwrap()) 340 .expect("note"); 341 let mut c = 0; 342 for block in blocks.iter(¬e) { 343 match c { 344 0 => { 345 assert_eq!(block.blocktype(), BlockType::Hashtag); 346 assert_eq!(block.as_str(), "hashtags"); 347 } 348 349 1 => { 350 assert_eq!(block.blocktype(), BlockType::Text); 351 assert_eq!(block.as_str(), ", are neat "); 352 } 353 354 2 => { 355 assert_eq!(block.blocktype(), BlockType::MentionBech32); 356 assert_eq!(block.as_str(), "nprofile1qqsr9cvzwc652r4m83d86ykplrnm9dg5gwdvzzn8ameanlvut35wy3gpz3mhxue69uhhyetvv9ujuerpd46hxtnfduyu75sw"); 357 match block.as_mention().unwrap() { 358 Mention::Profile(p) => assert_eq!(p.pubkey(), &pubkey_bytes), 359 _ => assert!(false), 360 }; 361 } 362 363 3 => { 364 assert_eq!(block.blocktype(), BlockType::Text); 365 assert_eq!(block.as_str(), " "); 366 } 367 368 4 => { 369 assert_eq!(block.blocktype(), BlockType::Url); 370 assert_eq!(block.as_str(), "https://github.com/damus-io"); 371 } 372 373 _ => assert!(false), 374 } 375 376 c += 1; 377 } 378 } 379 380 test_util::cleanup_db(&db); 381 } 382 }