lib

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

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 }