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