lib

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

decode.rs (4773B)


      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_REACTION, post::reaction::Reaction, social::SocialTarget,
      9     tag::name::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<Reaction, 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(Reaction {
     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: u64,
     46     kind: u32,
     47     content: String,
     48     tags: Vec<Vec<String>>,
     49 ) -> Result<RadrootsParsedData<Reaction>, 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: u64,
     64     kind: u32,
     65     content: String,
     66     tags: Vec<Vec<String>>,
     67     sig: String,
     68 ) -> Result<RadrootsParsedEvent<Reaction>, 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     RadrootsParsedEvent::from_event_parts(id, author, published_at, kind, content, tags, sig, data)
     78 }
     79 
     80 fn parse_reaction_target(tags: &[Vec<String>]) -> Result<SocialTarget, EventParseError> {
     81     let event_tag = find_tag(tags, "e");
     82     let address_tag = find_tag(tags, "a");
     83     match (event_tag, address_tag) {
     84         (Some(_), Some(_)) => Err(EventParseError::InvalidTag("e")),
     85         (None, None) => Err(EventParseError::MissingTag("e")),
     86         (Some(tag), None) => {
     87             let id = tag
     88                 .get(1)
     89                 .cloned()
     90                 .ok_or(EventParseError::InvalidTag("e"))?;
     91             validate_lowercase_hex_64_tag(&id, "e")?;
     92             let relays = if tag.len() > 2 {
     93                 Some(tag[2..].to_vec())
     94             } else {
     95                 None
     96             };
     97             Ok(SocialTarget::Event {
     98                 id,
     99                 author: optional_tag_value(tags, "p")?,
    100                 event_kind: optional_numeric_tag(tags, "k")?,
    101                 relays,
    102             })
    103         }
    104         (None, Some(tag)) => {
    105             let value = tag
    106                 .get(1)
    107                 .cloned()
    108                 .ok_or(EventParseError::InvalidTag("a"))?;
    109             let address = parse_address_tag(&value, "a")?;
    110             let kind = optional_numeric_tag(tags, "k")?.unwrap_or(address.kind);
    111             if kind != address.kind {
    112                 return Err(EventParseError::InvalidTag("k"));
    113             }
    114             let author = optional_tag_value(tags, "p")?.unwrap_or_else(|| address.pubkey.clone());
    115             if author != address.pubkey {
    116                 return Err(EventParseError::InvalidTag("p"));
    117             }
    118             let relays = if tag.len() > 2 {
    119                 Some(tag[2..].to_vec())
    120             } else {
    121                 None
    122             };
    123             Ok(SocialTarget::Address {
    124                 address: value,
    125                 author: Some(author),
    126                 event_kind: Some(kind),
    127                 relays,
    128             })
    129         }
    130     }
    131 }
    132 
    133 fn find_tag<'a>(tags: &'a [Vec<String>], key: &'static str) -> Option<&'a Vec<String>> {
    134     tags.iter()
    135         .find(|tag| tag.first().map(|value| value.as_str()) == Some(key))
    136 }
    137 
    138 fn optional_tag_value(
    139     tags: &[Vec<String>],
    140     key: &'static str,
    141 ) -> Result<Option<String>, EventParseError> {
    142     let Some(tag) = find_tag(tags, key) else {
    143         return Ok(None);
    144     };
    145     let value = tag
    146         .get(1)
    147         .cloned()
    148         .ok_or(EventParseError::InvalidTag(key))?;
    149     if value.trim().is_empty() {
    150         return Err(EventParseError::InvalidTag(key));
    151     }
    152     Ok(Some(value))
    153 }
    154 
    155 fn optional_numeric_tag(
    156     tags: &[Vec<String>],
    157     key: &'static str,
    158 ) -> Result<Option<u32>, EventParseError> {
    159     optional_tag_value(tags, key)?
    160         .map(|value| {
    161             value
    162                 .parse::<u32>()
    163                 .map_err(|err| EventParseError::InvalidNumber(key, err))
    164         })
    165         .transpose()
    166 }