decode.rs (4068B)
1 #[cfg(not(feature = "std"))] 2 use alloc::{string::String, vec::Vec}; 3 4 use radroots_events::{ 5 RadrootsNostrEvent, 6 kinds::{KIND_LIST_READ_WRITE_RELAYS, is_nip51_list_set_kind, is_nip51_standard_list_kind}, 7 list::{RadrootsList, RadrootsListEntry}, 8 tags::TAG_R, 9 }; 10 11 use crate::error::EventParseError; 12 use crate::parsed::{RadrootsParsedData, RadrootsParsedEvent}; 13 14 fn entry_from_tag(tag: &[String]) -> Result<RadrootsListEntry, EventParseError> { 15 let name = &tag[0]; 16 if name.trim().is_empty() { 17 return Err(EventParseError::InvalidTag("tag")); 18 } 19 let value = &tag[1]; 20 if value.trim().is_empty() { 21 return Err(EventParseError::InvalidTag("tag")); 22 } 23 Ok(RadrootsListEntry { 24 tag: name.clone(), 25 values: tag[1..].to_vec(), 26 }) 27 } 28 29 pub fn list_entries_from_tags( 30 tags: &[Vec<String>], 31 ) -> Result<Vec<RadrootsListEntry>, EventParseError> { 32 let mut entries = Vec::with_capacity(tags.len()); 33 for tag in tags.iter().filter(|t| t.len() >= 2) { 34 entries.push(entry_from_tag(tag)?); 35 } 36 Ok(entries) 37 } 38 39 pub fn list_from_tags( 40 kind: u32, 41 content: String, 42 tags: &[Vec<String>], 43 ) -> Result<RadrootsList, EventParseError> { 44 if !is_supported_list_kind(kind) { 45 return Err(EventParseError::InvalidKind { 46 expected: "nip51 standard or list-set kind", 47 got: kind, 48 }); 49 } 50 if kind == KIND_LIST_READ_WRITE_RELAYS { 51 validate_relay_tags(tags)?; 52 } 53 let entries = list_entries_from_tags(tags)?; 54 Ok(RadrootsList { content, entries }) 55 } 56 57 fn is_supported_list_kind(kind: u32) -> bool { 58 is_nip51_standard_list_kind(kind) || is_nip51_list_set_kind(kind) 59 } 60 61 fn validate_relay_tags(tags: &[Vec<String>]) -> Result<(), EventParseError> { 62 if tags.is_empty() { 63 return Err(EventParseError::MissingTag(TAG_R)); 64 } 65 for tag in tags { 66 if tag.first().map(|value| value.as_str()) != Some(TAG_R) { 67 return Err(EventParseError::InvalidTag(TAG_R)); 68 } 69 let Some(url) = tag.get(1) else { 70 return Err(EventParseError::InvalidTag(TAG_R)); 71 }; 72 if !is_ws_relay_url(url) { 73 return Err(EventParseError::InvalidTag(TAG_R)); 74 } 75 if tag.len() > 3 { 76 return Err(EventParseError::InvalidTag(TAG_R)); 77 } 78 if let Some(marker) = tag.get(2) 79 && marker != "read" 80 && marker != "write" 81 { 82 return Err(EventParseError::InvalidTag(TAG_R)); 83 } 84 } 85 Ok(()) 86 } 87 88 fn is_ws_relay_url(value: &str) -> bool { 89 (value.starts_with("wss://") && value.len() > "wss://".len()) 90 || (value.starts_with("ws://") && value.len() > "ws://".len()) 91 } 92 93 pub fn data_from_event( 94 id: String, 95 author: String, 96 published_at: u32, 97 kind: u32, 98 content: String, 99 tags: Vec<Vec<String>>, 100 ) -> Result<RadrootsParsedData<RadrootsList>, EventParseError> { 101 let list = list_from_tags(kind, content, &tags)?; 102 Ok(RadrootsParsedData::new( 103 id, 104 author, 105 published_at, 106 kind, 107 list, 108 )) 109 } 110 111 pub fn parsed_from_event( 112 id: String, 113 author: String, 114 published_at: u32, 115 kind: u32, 116 content: String, 117 tags: Vec<Vec<String>>, 118 sig: String, 119 ) -> Result<RadrootsParsedEvent<RadrootsList>, EventParseError> { 120 let data = data_from_event( 121 id.clone(), 122 author.clone(), 123 published_at, 124 kind, 125 content.clone(), 126 tags.clone(), 127 )?; 128 Ok(RadrootsParsedEvent { 129 event: RadrootsNostrEvent { 130 id, 131 author, 132 created_at: published_at, 133 kind, 134 content, 135 tags, 136 sig, 137 }, 138 data, 139 }) 140 } 141 142 #[cfg(feature = "serde_json")] 143 pub fn list_private_entries_from_json( 144 content: &str, 145 ) -> Result<Vec<RadrootsListEntry>, EventParseError> { 146 let tags: Vec<Vec<String>> = 147 serde_json::from_str(content).map_err(|_| EventParseError::InvalidJson("content"))?; 148 list_entries_from_tags(&tags) 149 }