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