event.rs (4253B)
1 use core::fmt; 2 3 use nostr::{Event, Kind}; 4 use radroots_event_codec::decode::job::{JobEventBorrow, JobEventLike}; 5 6 #[derive(Clone)] 7 pub struct NostrEventAdapter<'a> { 8 evt: &'a Event, 9 id_hex: String, 10 author_hex: String, 11 } 12 13 impl fmt::Debug for NostrEventAdapter<'_> { 14 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 15 formatter.write_str("NostrEventAdapter([redacted])") 16 } 17 } 18 19 impl<'a> NostrEventAdapter<'a> { 20 #[inline] 21 pub fn new(evt: &'a Event) -> Self { 22 Self { 23 evt, 24 id_hex: evt.id.to_hex(), 25 author_hex: evt.pubkey.to_string(), 26 } 27 } 28 29 #[inline] 30 fn tags_as_slices(&self) -> Vec<Vec<String>> { 31 self.evt 32 .tags 33 .iter() 34 .map(|t| t.as_slice().to_vec()) 35 .collect() 36 } 37 } 38 39 impl<'a> JobEventBorrow<'a> for NostrEventAdapter<'a> { 40 #[inline] 41 fn raw_id(&'a self) -> String { 42 self.id_hex.clone() 43 } 44 #[inline] 45 fn raw_author(&'a self) -> String { 46 self.author_hex.clone() 47 } 48 #[inline] 49 fn raw_content(&'a self) -> &'a str { 50 &self.evt.content 51 } 52 #[inline] 53 fn raw_kind(&'a self) -> u32 { 54 match self.evt.kind { 55 Kind::Custom(v) => v as u32, 56 _ => 0, 57 } 58 } 59 } 60 61 impl JobEventLike for NostrEventAdapter<'_> { 62 fn raw_id(&self) -> String { 63 self.id_hex.clone() 64 } 65 fn raw_author(&self) -> String { 66 self.author_hex.clone() 67 } 68 fn raw_published_at(&self) -> u64 { 69 self.evt.created_at.as_secs() 70 } 71 fn raw_kind(&self) -> u32 { 72 match self.evt.kind { 73 Kind::Custom(v) => v as u32, 74 _ => 0, 75 } 76 } 77 fn raw_content(&self) -> String { 78 self.evt.content.clone() 79 } 80 fn raw_tags(&self) -> Vec<Vec<String>> { 81 self.tags_as_slices() 82 } 83 fn raw_sig(&self) -> String { 84 self.evt.sig.to_string() 85 } 86 } 87 88 #[cfg(test)] 89 #[cfg_attr(coverage_nightly, coverage(off))] 90 mod tests { 91 use super::NostrEventAdapter; 92 use nostr::{Event, Keys, Kind, Tag, TagKind}; 93 use radroots_event_codec::decode::job::{JobEventBorrow, JobEventLike}; 94 use radroots_nostr::event::GenericBuilder; 95 96 fn build_event(keys: &Keys, kind: Kind, tags: Vec<Tag>) -> Event { 97 GenericBuilder::new(kind, "content") 98 .tags(tags) 99 .sign_with_keys(keys) 100 .expect("event must sign") 101 } 102 103 #[test] 104 fn adapter_exposes_borrow_and_owned_fields_for_custom_kind() { 105 let keys = Keys::generate(); 106 let recipient = Keys::generate(); 107 let recipient_hex = recipient.public_key().to_hex(); 108 let tags = vec![Tag::custom(TagKind::p(), vec![recipient_hex.clone()])]; 109 let event = build_event(&keys, Kind::Custom(5322), tags); 110 let adapter = NostrEventAdapter::new(&event); 111 assert_eq!(format!("{adapter:?}"), "NostrEventAdapter([redacted])"); 112 113 assert_eq!(JobEventBorrow::raw_id(&adapter), event.id.to_hex()); 114 assert_eq!( 115 JobEventBorrow::raw_author(&adapter), 116 event.pubkey.to_string() 117 ); 118 assert_eq!(JobEventBorrow::raw_content(&adapter), "content"); 119 assert_eq!(JobEventBorrow::raw_kind(&adapter), 5322); 120 121 assert_eq!(JobEventLike::raw_id(&adapter), event.id.to_hex()); 122 assert_eq!(JobEventLike::raw_author(&adapter), event.pubkey.to_string()); 123 assert_eq!(JobEventLike::raw_content(&adapter), "content".to_string()); 124 assert_eq!(JobEventLike::raw_kind(&adapter), 5322); 125 assert_eq!( 126 JobEventLike::raw_tags(&adapter), 127 vec![vec!["p".to_string(), recipient_hex]] 128 ); 129 assert_eq!(JobEventLike::raw_sig(&adapter), event.sig.to_string()); 130 } 131 132 #[test] 133 fn adapter_maps_non_custom_kind_to_zero() { 134 let keys = Keys::generate(); 135 let event = build_event(&keys, Kind::Repost, Vec::new()); 136 let adapter = NostrEventAdapter::new(&event); 137 138 assert_eq!(JobEventBorrow::raw_kind(&adapter), 0); 139 assert_eq!(JobEventLike::raw_kind(&adapter), 0); 140 assert_eq!( 141 JobEventLike::raw_published_at(&adapter), 142 event.created_at.as_secs() 143 ); 144 } 145 }