decode.rs (5824B)
1 #[cfg(not(feature = "std"))] 2 use alloc::{ 3 string::{String, ToString}, 4 vec::Vec, 5 }; 6 7 use radroots_events::{ 8 RadrootsNostrEvent, 9 kinds::KIND_MESSAGE_FILE, 10 message_file::{RadrootsMessageFile, RadrootsMessageFileDimensions}, 11 }; 12 13 use crate::error::EventParseError; 14 use crate::message::tags::{parse_recipients, parse_reply_tag, parse_subject_tag}; 15 use crate::parsed::{RadrootsParsedData, RadrootsParsedEvent}; 16 17 const DEFAULT_KIND: u32 = KIND_MESSAGE_FILE; 18 19 fn required_tag_value(tags: &[Vec<String>], key: &'static str) -> Result<String, EventParseError> { 20 let value = tags 21 .iter() 22 .find(|t| t.first().map(|s| s.as_str()) == Some(key)) 23 .and_then(|t| t.get(1)); 24 let value = value.ok_or(EventParseError::MissingTag(key))?; 25 if value.trim().is_empty() { 26 return Err(EventParseError::InvalidTag(key)); 27 } 28 Ok(value.clone()) 29 } 30 31 fn optional_tag_value( 32 tags: &[Vec<String>], 33 key: &'static str, 34 ) -> Result<Option<String>, EventParseError> { 35 let value = tags 36 .iter() 37 .find(|t| t.first().map(|s| s.as_str()) == Some(key)) 38 .and_then(|t| t.get(1)); 39 match value { 40 Some(value) if value.trim().is_empty() => Err(EventParseError::InvalidTag(key)), 41 Some(value) => Ok(Some(value.clone())), 42 None => Ok(None), 43 } 44 } 45 46 fn parse_dimensions(value: &str) -> Result<RadrootsMessageFileDimensions, EventParseError> { 47 let (w, h) = value 48 .split_once('x') 49 .ok_or(EventParseError::InvalidTag("dim"))?; 50 let w = w 51 .parse::<u32>() 52 .map_err(|_| EventParseError::InvalidTag("dim"))?; 53 let h = h 54 .parse::<u32>() 55 .map_err(|_| EventParseError::InvalidTag("dim"))?; 56 Ok(RadrootsMessageFileDimensions { w, h }) 57 } 58 59 fn parse_size(tags: &[Vec<String>]) -> Result<Option<u64>, EventParseError> { 60 let value = tags 61 .iter() 62 .find(|t| t.first().map(|s| s.as_str()) == Some("size")) 63 .and_then(|t| t.get(1)); 64 let Some(value) = value else { 65 return Ok(None); 66 }; 67 if value.trim().is_empty() { 68 return Err(EventParseError::InvalidTag("size")); 69 } 70 let size = value 71 .parse::<u64>() 72 .map_err(|e| EventParseError::InvalidNumber("size", e))?; 73 Ok(Some(size)) 74 } 75 76 fn parse_dimensions_tag( 77 tags: &[Vec<String>], 78 ) -> Result<Option<RadrootsMessageFileDimensions>, EventParseError> { 79 let value = tags 80 .iter() 81 .find(|t| t.first().map(|s| s.as_str()) == Some("dim")) 82 .and_then(|t| t.get(1)); 83 let Some(value) = value else { 84 return Ok(None); 85 }; 86 if value.trim().is_empty() { 87 return Err(EventParseError::InvalidTag("dim")); 88 } 89 Ok(Some(parse_dimensions(value)?)) 90 } 91 92 fn parse_fallbacks(tags: &[Vec<String>]) -> Result<Vec<String>, EventParseError> { 93 let mut fallbacks = Vec::new(); 94 for tag in tags 95 .iter() 96 .filter(|t| t.first().map(|s| s.as_str()) == Some("fallback")) 97 { 98 let value = tag.get(1).ok_or(EventParseError::InvalidTag("fallback"))?; 99 if value.trim().is_empty() { 100 return Err(EventParseError::InvalidTag("fallback")); 101 } 102 fallbacks.push(value.clone()); 103 } 104 Ok(fallbacks) 105 } 106 107 pub fn message_file_from_tags( 108 kind: u32, 109 tags: &[Vec<String>], 110 content: &str, 111 ) -> Result<RadrootsMessageFile, EventParseError> { 112 if kind != DEFAULT_KIND { 113 return Err(EventParseError::InvalidKind { 114 expected: "15", 115 got: kind, 116 }); 117 } 118 if content.trim().is_empty() { 119 return Err(EventParseError::InvalidTag("content")); 120 } 121 122 let recipients = parse_recipients(tags)?; 123 let reply_to = parse_reply_tag(tags)?; 124 let subject = parse_subject_tag(tags)?; 125 let file_type = required_tag_value(tags, "file-type")?; 126 let encryption_algorithm = required_tag_value(tags, "encryption-algorithm")?; 127 let decryption_key = required_tag_value(tags, "decryption-key")?; 128 let decryption_nonce = required_tag_value(tags, "decryption-nonce")?; 129 let encrypted_hash = required_tag_value(tags, "x")?; 130 let original_hash = optional_tag_value(tags, "ox")?; 131 let size = parse_size(tags)?; 132 let dimensions = parse_dimensions_tag(tags)?; 133 let blurhash = optional_tag_value(tags, "blurhash")?; 134 let thumb = optional_tag_value(tags, "thumb")?; 135 let fallbacks = parse_fallbacks(tags)?; 136 137 Ok(RadrootsMessageFile { 138 recipients, 139 file_url: content.to_string(), 140 reply_to, 141 subject, 142 file_type, 143 encryption_algorithm, 144 decryption_key, 145 decryption_nonce, 146 encrypted_hash, 147 original_hash, 148 size, 149 dimensions, 150 blurhash, 151 thumb, 152 fallbacks, 153 }) 154 } 155 156 pub fn data_from_event( 157 id: String, 158 author: String, 159 published_at: u32, 160 kind: u32, 161 content: String, 162 tags: Vec<Vec<String>>, 163 ) -> Result<RadrootsParsedData<RadrootsMessageFile>, EventParseError> { 164 let message_file = message_file_from_tags(kind, &tags, &content)?; 165 Ok(RadrootsParsedData::new( 166 id, 167 author, 168 published_at, 169 kind, 170 message_file, 171 )) 172 } 173 174 pub fn parsed_from_event( 175 id: String, 176 author: String, 177 published_at: u32, 178 kind: u32, 179 content: String, 180 tags: Vec<Vec<String>>, 181 sig: String, 182 ) -> Result<RadrootsParsedEvent<RadrootsMessageFile>, EventParseError> { 183 let data = data_from_event( 184 id.clone(), 185 author.clone(), 186 published_at, 187 kind, 188 content.clone(), 189 tags.clone(), 190 )?; 191 Ok(RadrootsParsedEvent { 192 event: RadrootsNostrEvent { 193 id, 194 author, 195 created_at: published_at, 196 kind, 197 content, 198 tags, 199 sig, 200 }, 201 data, 202 }) 203 }