tangle_indexer


git clone https://radroots.dev/git/tangle_indexer.git
Log | Files | Refs | Submodules | LICENSE

reaction.rs (6045B)


      1 use thiserror::Error;
      2 
      3 use radroots_events::{
      4     reaction::{
      5         RadrootsReaction, RadrootsReactionEventIndex, RadrootsReactionEventMetadata,
      6     },
      7     RadrootsNostrEvent, RadrootsNostrEventRef,
      8 };
      9 
     10 use crate::relay::event::RelayIndexerEvent;
     11 
     12 #[derive(Debug, Error)]
     13 pub enum RadrootsReactionEventIndexError {
     14     #[error("Failed to parse reaction from tags")]
     15     ParseError,
     16 }
     17 
     18 fn parse_reaction_from_tags(
     19     tags: &[Vec<String>],
     20     content: &str,
     21 ) -> Result<RadrootsReaction, RadrootsReactionEventIndexError> {
     22     let parse_address = |addr: &str| -> Option<(u32, String, Option<String>)> {
     23         let mut parts = addr.splitn(3, ':');
     24         let kind = parts.next()?.parse::<u32>().ok()?;
     25         let author = parts.next()?.to_lowercase();
     26         let d_tag = parts
     27             .next()
     28             .and_then(|d| if d.is_empty() { None } else { Some(d.to_string()) });
     29         Some((kind, author, d_tag))
     30     };
     31 
     32     let mut root_id: Option<String> = None;
     33     let mut root_relays_e: Option<Vec<String>> = None;
     34     let mut root_relays_a: Option<Vec<String>> = None;
     35     let mut root_kind_tag: Option<u32> = None;
     36     let mut root_kind_addr: Option<u32> = None;
     37     let mut root_author_tag: Option<String> = None;
     38     let mut root_author_addr: Option<String> = None;
     39     let mut root_author_e: Option<String> = None;
     40     let mut root_d: Option<String> = None;
     41 
     42     for t in tags {
     43         match t.first().map(|k| k.as_str()) {
     44             Some("e") => {
     45                 if let Some(id) = t.get(1).cloned() {
     46                     root_id = Some(id);
     47                 }
     48                 if let Some(r) = t.get(2).filter(|s| !s.is_empty()).cloned() {
     49                     root_relays_e = Some(vec![r]);
     50                 }
     51                 if let Some(pk) = t.get(3).filter(|s| !s.is_empty()) {
     52                     root_author_e = Some(pk.to_lowercase());
     53                 }
     54             }
     55             Some("a") => {
     56                 if let Some(addr) = t.get(1).cloned() {
     57                     if let Some((kind, author, d_tag)) = parse_address(&addr) {
     58                         root_kind_addr = Some(kind);
     59                         root_author_addr = Some(author);
     60                         root_d = d_tag;
     61                     }
     62                 }
     63                 if let Some(r) = t.get(2).filter(|s| !s.is_empty()).cloned() {
     64                     root_relays_a = Some(vec![r]);
     65                 }
     66             }
     67             Some("k") => {
     68                 if let Some(kind) = t.get(1).and_then(|v| v.parse::<u32>().ok()) {
     69                     root_kind_tag = Some(kind);
     70                 }
     71             }
     72             Some("p") => {
     73                 if let Some(pk) = t.get(1).filter(|s| !s.is_empty()) {
     74                     root_author_tag = Some(pk.to_lowercase());
     75                 }
     76             }
     77             _ => {}
     78         }
     79     }
     80 
     81     let id = root_id.ok_or(RadrootsReactionEventIndexError::ParseError)?;
     82     let kind = root_kind_tag.or(root_kind_addr).unwrap_or(1);
     83     let author = root_author_tag
     84         .or(root_author_addr)
     85         .or(root_author_e)
     86         .unwrap_or_default();
     87     let relays = root_relays_e.or(root_relays_a);
     88 
     89     let root = RadrootsNostrEventRef {
     90         id,
     91         author,
     92         kind,
     93         d_tag: root_d,
     94         relays,
     95     };
     96 
     97     Ok(RadrootsReaction {
     98         root,
     99         content: content.to_string(),
    100     })
    101 }
    102 
    103 fn create_radroots_reaction_event_metadata(
    104     id: String,
    105     author: String,
    106     published_at: u32,
    107     kind: u32,
    108     content: &str,
    109     tags: &[Vec<String>],
    110 ) -> Result<RadrootsReactionEventMetadata, RadrootsReactionEventIndexError> {
    111     let reaction = parse_reaction_from_tags(tags, content)?;
    112     Ok(RadrootsReactionEventMetadata {
    113         id,
    114         author,
    115         published_at,
    116         kind,
    117         reaction,
    118     })
    119 }
    120 
    121 pub trait ToRadrootsReactionEventIndex {
    122     fn to_radroots_reaction_event(
    123         &self,
    124     ) -> Result<RadrootsReactionEventIndex, RadrootsReactionEventIndexError>;
    125 }
    126 
    127 impl ToRadrootsReactionEventIndex for RelayIndexerEvent {
    128     fn to_radroots_reaction_event(
    129         &self,
    130     ) -> Result<RadrootsReactionEventIndex, RadrootsReactionEventIndexError> {
    131         let kind_u32 = self.kind.as_u64() as u32;
    132         let id = self.id.clone();
    133         let author = self.author.clone();
    134 
    135         let metadata = create_radroots_reaction_event_metadata(
    136             id.clone(),
    137             author.clone(),
    138             self.created_at,
    139             kind_u32,
    140             &self.content,
    141             &self.tags,
    142         )?;
    143 
    144         Ok(RadrootsReactionEventIndex {
    145             event: RadrootsNostrEvent {
    146                 id,
    147                 author,
    148                 created_at: self.created_at,
    149                 kind: kind_u32,
    150                 tags: self.tags.clone(),
    151                 content: self.content.clone(),
    152                 sig: self.sig.clone(),
    153             },
    154             metadata,
    155         })
    156     }
    157 }
    158 
    159 #[cfg(test)]
    160 mod tests {
    161     use super::parse_reaction_from_tags;
    162 
    163     #[test]
    164     fn reaction_parses_event_reference() {
    165         let tags = vec![
    166             vec!["e".to_string(), "root123".to_string()],
    167             vec!["k".to_string(), "1".to_string()],
    168             vec!["p".to_string(), "a".repeat(64)],
    169         ];
    170         let reaction = parse_reaction_from_tags(&tags, "+").expect("parse reaction");
    171         assert_eq!(reaction.root.id, "root123");
    172         assert_eq!(reaction.root.kind, 1);
    173     }
    174 
    175     #[test]
    176     fn reaction_parses_address_reference() {
    177         let pubkey = "b".repeat(64);
    178         let addr = format!("30023:{}:dtag", pubkey);
    179         let tags = vec![
    180             vec!["e".to_string(), "root123".to_string()],
    181             vec!["a".to_string(), addr.clone()],
    182             vec!["k".to_string(), "30023".to_string()],
    183             vec!["p".to_string(), pubkey.clone()],
    184         ];
    185         let reaction = parse_reaction_from_tags(&tags, "+").expect("parse reaction");
    186         assert_eq!(reaction.root.kind, 30023);
    187         assert_eq!(reaction.root.author, pubkey);
    188         assert_eq!(reaction.root.d_tag.as_deref(), Some("dtag"));
    189     }
    190 }