decode.rs (4022B)
1 #[cfg(not(feature = "std"))] 2 use alloc::{string::String, vec::Vec}; 3 4 use radroots_event::{social::list::ListEntry, social::list_set::ListSet}; 5 6 use crate::error::EventParseError; 7 #[cfg(feature = "json")] 8 use crate::list::decode::list_entries_from_tags; 9 use crate::parsed::{RadrootsParsedData, RadrootsParsedEvent}; 10 11 const TAG_D: &str = "d"; 12 const TAG_TITLE: &str = "title"; 13 const TAG_DESCRIPTION: &str = "description"; 14 const TAG_IMAGE: &str = "image"; 15 16 fn entry_from_tag(tag: &[String]) -> Result<ListEntry, EventParseError> { 17 let name = &tag[0]; 18 let value = &tag[1]; 19 if value.trim().is_empty() { 20 return Err(EventParseError::InvalidTag("tag")); 21 } 22 Ok(ListEntry { 23 tag: name.clone(), 24 values: tag[1..].to_vec(), 25 }) 26 } 27 28 fn take_first_non_empty(tag: &[String]) -> Option<String> { 29 tag.get(1).filter(|v| !v.trim().is_empty()).cloned() 30 } 31 32 pub fn list_set_from_tags( 33 kind: u32, 34 content: String, 35 tags: &[Vec<String>], 36 ) -> Result<ListSet, EventParseError> { 37 if !super::is_generic_list_set_codec_kind(kind) { 38 return Err(EventParseError::InvalidKind { 39 expected: "generic nip51 list-set kind", 40 got: kind, 41 }); 42 } 43 let mut d_tag: Option<String> = None; 44 let mut title: Option<String> = None; 45 let mut description: Option<String> = None; 46 let mut image: Option<String> = None; 47 let mut entries = Vec::new(); 48 49 for tag in tags.iter().filter(|t| t.len() >= 2) { 50 let name = &tag[0]; 51 if name.trim().is_empty() { 52 return Err(EventParseError::InvalidTag("tag")); 53 } 54 match name.as_str() { 55 TAG_D => { 56 if d_tag.is_none() { 57 let value = &tag[1]; 58 if value.trim().is_empty() { 59 return Err(EventParseError::InvalidTag("d")); 60 } 61 d_tag = Some(value.clone()); 62 } 63 } 64 TAG_TITLE => { 65 if title.is_none() { 66 title = take_first_non_empty(tag); 67 } 68 } 69 TAG_DESCRIPTION => { 70 if description.is_none() { 71 description = take_first_non_empty(tag); 72 } 73 } 74 TAG_IMAGE => { 75 if image.is_none() { 76 image = take_first_non_empty(tag); 77 } 78 } 79 _ => { 80 entries.push(entry_from_tag(tag)?); 81 } 82 } 83 } 84 85 let d_tag = d_tag.ok_or(EventParseError::MissingTag("d"))?; 86 if !super::list_set_base64_id_is_valid(&d_tag) { 87 return Err(EventParseError::InvalidTag("d")); 88 } 89 Ok(ListSet { 90 d_tag, 91 content, 92 entries, 93 title, 94 description, 95 image, 96 }) 97 } 98 99 pub fn data_from_event( 100 id: String, 101 author: String, 102 published_at: u64, 103 kind: u32, 104 content: String, 105 tags: Vec<Vec<String>>, 106 ) -> Result<RadrootsParsedData<ListSet>, EventParseError> { 107 let list_set = list_set_from_tags(kind, content, &tags)?; 108 Ok(RadrootsParsedData::new( 109 id, 110 author, 111 published_at, 112 kind, 113 list_set, 114 )) 115 } 116 117 pub fn parsed_from_event( 118 id: String, 119 author: String, 120 published_at: u64, 121 kind: u32, 122 content: String, 123 tags: Vec<Vec<String>>, 124 sig: String, 125 ) -> Result<RadrootsParsedEvent<ListSet>, EventParseError> { 126 let data = data_from_event( 127 id.clone(), 128 author.clone(), 129 published_at, 130 kind, 131 content.clone(), 132 tags.clone(), 133 )?; 134 RadrootsParsedEvent::from_event_parts(id, author, published_at, kind, content, tags, sig, data) 135 } 136 137 #[cfg(feature = "json")] 138 pub fn list_set_private_entries_from_json( 139 content: &str, 140 ) -> Result<Vec<ListEntry>, EventParseError> { 141 let tags: Vec<Vec<String>> = 142 serde_json::from_str(content).map_err(|_| EventParseError::InvalidJson("content"))?; 143 list_entries_from_tags(&tags) 144 }