decode.rs (4991B)
1 #[cfg(not(feature = "std"))] 2 use alloc::{ 3 string::{String, ToString}, 4 vec::Vec, 5 }; 6 7 use radroots_events::{ 8 RadrootsNostrEvent, kinds::KIND_REACTION, reaction::RadrootsReaction, 9 social::RadrootsSocialTarget, tags::TAG_E_ROOT, 10 }; 11 12 use crate::error::EventParseError; 13 use crate::field_helpers::{parse_address_tag, validate_lowercase_hex_64_tag}; 14 use crate::parsed::{RadrootsParsedData, RadrootsParsedEvent}; 15 16 const DEFAULT_KIND: u32 = KIND_REACTION; 17 18 pub fn reaction_from_tags( 19 kind: u32, 20 tags: &[Vec<String>], 21 content: &str, 22 ) -> Result<RadrootsReaction, EventParseError> { 23 if kind != DEFAULT_KIND { 24 return Err(EventParseError::InvalidKind { 25 expected: "7", 26 got: kind, 27 }); 28 } 29 if tags 30 .iter() 31 .any(|tag| tag.first().map(|value| value.as_str()) == Some(TAG_E_ROOT)) 32 { 33 return Err(EventParseError::InvalidTag(TAG_E_ROOT)); 34 } 35 let target = parse_reaction_target(tags)?; 36 Ok(RadrootsReaction { 37 target, 38 content: content.to_string(), 39 }) 40 } 41 42 pub fn data_from_event( 43 id: String, 44 author: String, 45 published_at: u32, 46 kind: u32, 47 content: String, 48 tags: Vec<Vec<String>>, 49 ) -> Result<RadrootsParsedData<RadrootsReaction>, EventParseError> { 50 let reaction = reaction_from_tags(kind, &tags, &content)?; 51 Ok(RadrootsParsedData::new( 52 id, 53 author, 54 published_at, 55 kind, 56 reaction, 57 )) 58 } 59 60 pub fn parsed_from_event( 61 id: String, 62 author: String, 63 published_at: u32, 64 kind: u32, 65 content: String, 66 tags: Vec<Vec<String>>, 67 sig: String, 68 ) -> Result<RadrootsParsedEvent<RadrootsReaction>, EventParseError> { 69 let data = data_from_event( 70 id.clone(), 71 author.clone(), 72 published_at, 73 kind, 74 content.clone(), 75 tags.clone(), 76 )?; 77 Ok(RadrootsParsedEvent { 78 event: RadrootsNostrEvent { 79 id, 80 author, 81 created_at: published_at, 82 kind, 83 content, 84 tags, 85 sig, 86 }, 87 data, 88 }) 89 } 90 91 fn parse_reaction_target(tags: &[Vec<String>]) -> Result<RadrootsSocialTarget, EventParseError> { 92 let event_tag = find_tag(tags, "e"); 93 let address_tag = find_tag(tags, "a"); 94 match (event_tag, address_tag) { 95 (Some(_), Some(_)) => Err(EventParseError::InvalidTag("e")), 96 (None, None) => Err(EventParseError::MissingTag("e")), 97 (Some(tag), None) => { 98 let id = tag 99 .get(1) 100 .cloned() 101 .ok_or(EventParseError::InvalidTag("e"))?; 102 validate_lowercase_hex_64_tag(&id, "e")?; 103 let relays = if tag.len() > 2 { 104 Some(tag[2..].to_vec()) 105 } else { 106 None 107 }; 108 Ok(RadrootsSocialTarget::Event { 109 id, 110 author: optional_tag_value(tags, "p")?, 111 event_kind: optional_numeric_tag(tags, "k")?, 112 relays, 113 }) 114 } 115 (None, Some(tag)) => { 116 let value = tag 117 .get(1) 118 .cloned() 119 .ok_or(EventParseError::InvalidTag("a"))?; 120 let address = parse_address_tag(&value, "a")?; 121 let kind = optional_numeric_tag(tags, "k")?.unwrap_or(address.kind); 122 if kind != address.kind { 123 return Err(EventParseError::InvalidTag("k")); 124 } 125 let author = optional_tag_value(tags, "p")?.unwrap_or_else(|| address.pubkey.clone()); 126 if author != address.pubkey { 127 return Err(EventParseError::InvalidTag("p")); 128 } 129 let relays = if tag.len() > 2 { 130 Some(tag[2..].to_vec()) 131 } else { 132 None 133 }; 134 Ok(RadrootsSocialTarget::Address { 135 address: value, 136 author: Some(author), 137 event_kind: Some(kind), 138 relays, 139 }) 140 } 141 } 142 } 143 144 fn find_tag<'a>(tags: &'a [Vec<String>], key: &'static str) -> Option<&'a Vec<String>> { 145 tags.iter() 146 .find(|tag| tag.first().map(|value| value.as_str()) == Some(key)) 147 } 148 149 fn optional_tag_value( 150 tags: &[Vec<String>], 151 key: &'static str, 152 ) -> Result<Option<String>, EventParseError> { 153 let Some(tag) = find_tag(tags, key) else { 154 return Ok(None); 155 }; 156 let value = tag 157 .get(1) 158 .cloned() 159 .ok_or(EventParseError::InvalidTag(key))?; 160 if value.trim().is_empty() { 161 return Err(EventParseError::InvalidTag(key)); 162 } 163 Ok(Some(value)) 164 } 165 166 fn optional_numeric_tag( 167 tags: &[Vec<String>], 168 key: &'static str, 169 ) -> Result<Option<u32>, EventParseError> { 170 optional_tag_value(tags, key)? 171 .map(|value| { 172 value 173 .parse::<u32>() 174 .map_err(|err| EventParseError::InvalidNumber(key, err)) 175 }) 176 .transpose() 177 }