lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

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 }