tags.rs (7554B)
1 use crate::{bindings, NdbStr, Note}; 2 3 #[derive(Debug, Clone)] 4 pub struct Tag<'n> { 5 ptr: *mut bindings::ndb_tag, 6 note: Note<'n>, 7 } 8 9 impl<'n> Tag<'n> { 10 pub(crate) fn new(ptr: *mut bindings::ndb_tag, note: Note<'n>) -> Self { 11 Tag { ptr, note } 12 } 13 14 pub fn count(&self) -> u16 { 15 unsafe { bindings::ndb_tag_count(self.as_ptr()) } 16 } 17 18 pub fn get_unchecked(&self, ind: u16) -> NdbStr<'n> { 19 let nstr = unsafe { 20 bindings::ndb_tag_str( 21 self.note().as_ptr(), 22 self.as_ptr(), 23 ind as ::std::os::raw::c_int, 24 ) 25 }; 26 NdbStr::new(nstr, self.note.clone()) 27 } 28 29 pub fn get_str(&self, ind: u16) -> Option<&'n str> { 30 self.get(ind).and_then(|s| s.str()) 31 } 32 33 pub fn get_id(&self, ind: u16) -> Option<&'n [u8; 32]> { 34 self.get(ind).and_then(|s| s.id()) 35 } 36 37 pub fn get(&self, ind: u16) -> Option<NdbStr<'n>> { 38 if ind >= self.count() { 39 return None; 40 } 41 Some(self.get_unchecked(ind)) 42 } 43 44 pub fn note(&self) -> &Note<'n> { 45 &self.note 46 } 47 48 pub fn as_ptr(&self) -> *mut bindings::ndb_tag { 49 self.ptr 50 } 51 } 52 53 impl<'a> IntoIterator for Tag<'a> { 54 type Item = NdbStr<'a>; 55 type IntoIter = TagIter<'a>; 56 57 fn into_iter(self) -> Self::IntoIter { 58 TagIter::new(self) 59 } 60 } 61 62 #[derive(Debug, Clone)] 63 pub struct Tags<'a> { 64 ptr: *mut bindings::ndb_tags, 65 note: Note<'a>, 66 } 67 68 impl<'a> IntoIterator for Tags<'a> { 69 type Item = Tag<'a>; 70 type IntoIter = TagsIter<'a>; 71 72 fn into_iter(self) -> TagsIter<'a> { 73 TagsIter::new(self.note().clone()) 74 } 75 } 76 77 impl<'a> Tags<'a> { 78 pub(crate) fn new(ptr: *mut bindings::ndb_tags, note: Note<'a>) -> Self { 79 Tags { ptr, note } 80 } 81 82 pub fn count(&self) -> u16 { 83 unsafe { bindings::ndb_tags_count(self.as_ptr()) } 84 } 85 86 pub fn iter(&self) -> TagsIter<'a> { 87 TagsIter::new(self.note.clone()) 88 } 89 90 pub fn note(&self) -> &Note<'a> { 91 &self.note 92 } 93 94 pub fn as_ptr(&self) -> *mut bindings::ndb_tags { 95 self.ptr 96 } 97 } 98 99 #[derive(Debug, Clone)] 100 pub struct TagsIter<'a> { 101 iter: bindings::ndb_iterator, 102 note: Note<'a>, 103 } 104 105 impl<'a> TagsIter<'a> { 106 pub fn new(note: Note<'a>) -> Self { 107 let iter = bindings::ndb_iterator { 108 note: std::ptr::null_mut(), 109 tag: std::ptr::null_mut(), 110 index: 0, 111 }; 112 let mut iter = TagsIter { note, iter }; 113 unsafe { 114 bindings::ndb_tags_iterate_start(iter.note.as_ptr(), &mut iter.iter); 115 }; 116 iter 117 } 118 119 pub fn tag(&self) -> Option<Tag<'a>> { 120 let tag_ptr = unsafe { *self.as_ptr() }.tag; 121 if tag_ptr.is_null() { 122 None 123 } else { 124 Some(Tag::new(tag_ptr, self.note().clone())) 125 } 126 } 127 128 pub fn note(&self) -> &Note<'a> { 129 &self.note 130 } 131 132 pub fn as_ptr(&self) -> *const bindings::ndb_iterator { 133 &self.iter 134 } 135 136 pub fn as_mut_ptr(&mut self) -> *mut bindings::ndb_iterator { 137 &mut self.iter 138 } 139 } 140 141 #[derive(Debug, Clone)] 142 pub struct TagIter<'a> { 143 tag: Tag<'a>, 144 index: u16, 145 } 146 147 impl<'a> TagIter<'a> { 148 pub fn new(tag: Tag<'a>) -> Self { 149 let index = 0; 150 TagIter { tag, index } 151 } 152 153 pub fn done(&self) -> bool { 154 self.index >= self.tag.count() 155 } 156 } 157 158 impl<'a> Iterator for TagIter<'a> { 159 type Item = NdbStr<'a>; 160 161 fn next(&mut self) -> Option<NdbStr<'a>> { 162 let tag = self.tag.get(self.index); 163 if tag.is_some() { 164 self.index += 1; 165 tag 166 } else { 167 None 168 } 169 } 170 } 171 172 impl<'a> Iterator for TagsIter<'a> { 173 type Item = Tag<'a>; 174 175 fn next(&mut self) -> Option<Self::Item> { 176 let res = unsafe { bindings::ndb_tags_iterate_next(self.as_mut_ptr()) }; 177 if res == 0 { 178 None 179 } else { 180 self.tag() 181 } 182 } 183 } 184 185 #[cfg(test)] 186 mod tests { 187 use crate::config::Config; 188 use crate::test_util; 189 use crate::{Filter, Ndb, NdbStrVariant, Transaction}; 190 191 #[tokio::test] 192 async fn tag_iter_works() { 193 let db = "target/testdbs/tag_iter_works"; 194 test_util::cleanup_db(&db); 195 196 { 197 let ndb = Ndb::new(db, &Config::new()).expect("ndb"); 198 let sub = ndb 199 .subscribe(&[Filter::new() 200 .ids([&[ 201 0xc5, 0xd9, 0x8c, 0xbf, 0x4b, 0xcd, 0x81, 0x1e, 0x28, 0x66, 0x77, 0x0c, 202 0x3d, 0x38, 0x0c, 0x02, 0x84, 0xce, 0x1d, 0xaf, 0x3a, 0xe9, 0x98, 0x3d, 203 0x22, 0x56, 0x5c, 0xb0, 0x66, 0xcf, 0x2a, 0x19, 204 ]]) 205 .build()]) 206 .expect("sub"); 207 let waiter = ndb.wait_for_notes(sub, 1); 208 ndb.process_event(r#"["EVENT","s",{"id": "c5d98cbf4bcd811e2866770c3d380c0284ce1daf3ae9983d22565cb066cf2a19","pubkey": "083727b7a6051673f399102dc48c229c0ec08186ecd7e54ad0e9116d38429c4f","created_at": 1712517119,"kind": 1,"tags": [["e","b9e548b4aa30fa4ce9edf552adaf458385716704994fbaa9e0aa0042a5a5e01e"],["p","140ee9ff21da6e6671f750a0a747c5a3487ee8835159c7ca863e867a1c537b4f"],["hi","3"]],"content": "hi","sig": "1eed792e4db69c2bde2f5be33a383ef8b17c6afd1411598d0c4618fbdf4dbcb9689354276a74614511907a45eec234e0786733e8a6fbb312e6abf153f15fd437"}]"#).expect("process ok"); 209 let res = waiter.await.expect("await ok"); 210 assert_eq!(res.len(), 1); 211 let note_key = res[0]; 212 let txn = Transaction::new(&ndb).expect("txn"); 213 let note = ndb.get_note_by_key(&txn, note_key).expect("note"); 214 let tags = note.tags(); 215 assert_eq!(tags.count(), 3); 216 217 let mut tags_iter = tags.iter(); 218 219 let t0 = tags_iter.next().expect("t0"); 220 let t0_e0 = t0.get(0).expect("e tag ok"); 221 let t0_e1 = t0.get(1).expect("e id ok"); 222 assert_eq!(t0.get(2).is_none(), true); 223 assert_eq!(t0_e0.variant(), NdbStrVariant::Str("e")); 224 assert_eq!( 225 t0_e1.variant(), 226 NdbStrVariant::Id(&[ 227 0xb9, 0xe5, 0x48, 0xb4, 0xaa, 0x30, 0xfa, 0x4c, 0xe9, 0xed, 0xf5, 0x52, 0xad, 228 0xaf, 0x45, 0x83, 0x85, 0x71, 0x67, 0x04, 0x99, 0x4f, 0xba, 0xa9, 0xe0, 0xaa, 229 0x00, 0x42, 0xa5, 0xa5, 0xe0, 0x1e 230 ]) 231 ); 232 233 let t1 = tags_iter.next().expect("t1"); 234 let t1_e0 = t1.get(0).expect("p tag ok"); 235 let t1_e1 = t1.get(1).expect("p id ok"); 236 assert_eq!(t1.get(2).is_none(), true); 237 assert_eq!(t1_e0.variant(), NdbStrVariant::Str("p")); 238 assert_eq!( 239 t1_e1.variant(), 240 NdbStrVariant::Id(&[ 241 0x14, 0x0e, 0xe9, 0xff, 0x21, 0xda, 0x6e, 0x66, 0x71, 0xf7, 0x50, 0xa0, 0xa7, 242 0x47, 0xc5, 0xa3, 0x48, 0x7e, 0xe8, 0x83, 0x51, 0x59, 0xc7, 0xca, 0x86, 0x3e, 243 0x86, 0x7a, 0x1c, 0x53, 0x7b, 0x4f 244 ]) 245 ); 246 247 let t2 = tags_iter.next().expect("t2"); 248 let t2_e0 = t2.get(0).expect("hi tag ok"); 249 let t2_e1 = t2.get(1).expect("hi value ok"); 250 assert_eq!(t2.get(2).is_none(), true); 251 assert_eq!(t2_e0.variant(), NdbStrVariant::Str("hi")); 252 assert_eq!(t2_e1.variant(), NdbStrVariant::Str("3")); 253 254 assert_eq!(tags_iter.next().is_none(), true); 255 } 256 } 257 }