lib

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

decode.rs (3709B)


      1 #[cfg(not(feature = "std"))]
      2 use alloc::{string::String, vec::Vec};
      3 
      4 use radroots_event::{
      5     envelope::kind::KIND_LIST_READ_WRITE_RELAYS,
      6     social::list::{List, ListEntry},
      7     tag::name::TAG_R,
      8 };
      9 
     10 use super::is_generic_list_codec_kind;
     11 use crate::error::EventParseError;
     12 use crate::parsed::{RadrootsParsedData, RadrootsParsedEvent};
     13 
     14 fn entry_from_tag(tag: &[String]) -> Result<ListEntry, EventParseError> {
     15     let name = &tag[0];
     16     if name.trim().is_empty() {
     17         return Err(EventParseError::InvalidTag("tag"));
     18     }
     19     let value = &tag[1];
     20     if value.trim().is_empty() {
     21         return Err(EventParseError::InvalidTag("tag"));
     22     }
     23     Ok(ListEntry {
     24         tag: name.clone(),
     25         values: tag[1..].to_vec(),
     26     })
     27 }
     28 
     29 pub fn list_entries_from_tags(tags: &[Vec<String>]) -> Result<Vec<ListEntry>, EventParseError> {
     30     let mut entries = Vec::with_capacity(tags.len());
     31     for tag in tags.iter().filter(|t| t.len() >= 2) {
     32         entries.push(entry_from_tag(tag)?);
     33     }
     34     Ok(entries)
     35 }
     36 
     37 pub fn list_from_tags(
     38     kind: u32,
     39     content: String,
     40     tags: &[Vec<String>],
     41 ) -> Result<List, EventParseError> {
     42     if !is_generic_list_codec_kind(kind) {
     43         return Err(EventParseError::InvalidKind {
     44             expected: "generic nip51 standard or non-calendar list-set kind",
     45             got: kind,
     46         });
     47     }
     48     if kind == KIND_LIST_READ_WRITE_RELAYS {
     49         validate_relay_tags(tags)?;
     50     }
     51     let entries = list_entries_from_tags(tags)?;
     52     Ok(List { content, entries })
     53 }
     54 
     55 fn validate_relay_tags(tags: &[Vec<String>]) -> Result<(), EventParseError> {
     56     if tags.is_empty() {
     57         return Err(EventParseError::MissingTag(TAG_R));
     58     }
     59     for tag in tags {
     60         if tag.first().map(|value| value.as_str()) != Some(TAG_R) {
     61             return Err(EventParseError::InvalidTag(TAG_R));
     62         }
     63         let Some(url) = tag.get(1) else {
     64             return Err(EventParseError::InvalidTag(TAG_R));
     65         };
     66         if !is_ws_relay_url(url) {
     67             return Err(EventParseError::InvalidTag(TAG_R));
     68         }
     69         if tag.len() > 3 {
     70             return Err(EventParseError::InvalidTag(TAG_R));
     71         }
     72         if let Some(marker) = tag.get(2)
     73             && marker != "read"
     74             && marker != "write"
     75         {
     76             return Err(EventParseError::InvalidTag(TAG_R));
     77         }
     78     }
     79     Ok(())
     80 }
     81 
     82 fn is_ws_relay_url(value: &str) -> bool {
     83     (value.starts_with("wss://") && value.len() > "wss://".len())
     84         || (value.starts_with("ws://") && value.len() > "ws://".len())
     85 }
     86 
     87 pub fn data_from_event(
     88     id: String,
     89     author: String,
     90     published_at: u64,
     91     kind: u32,
     92     content: String,
     93     tags: Vec<Vec<String>>,
     94 ) -> Result<RadrootsParsedData<List>, EventParseError> {
     95     let list = list_from_tags(kind, content, &tags)?;
     96     Ok(RadrootsParsedData::new(
     97         id,
     98         author,
     99         published_at,
    100         kind,
    101         list,
    102     ))
    103 }
    104 
    105 pub fn parsed_from_event(
    106     id: String,
    107     author: String,
    108     published_at: u64,
    109     kind: u32,
    110     content: String,
    111     tags: Vec<Vec<String>>,
    112     sig: String,
    113 ) -> Result<RadrootsParsedEvent<List>, EventParseError> {
    114     let data = data_from_event(
    115         id.clone(),
    116         author.clone(),
    117         published_at,
    118         kind,
    119         content.clone(),
    120         tags.clone(),
    121     )?;
    122     RadrootsParsedEvent::from_event_parts(id, author, published_at, kind, content, tags, sig, data)
    123 }
    124 
    125 #[cfg(feature = "json")]
    126 pub fn list_private_entries_from_json(content: &str) -> Result<Vec<ListEntry>, EventParseError> {
    127     let tags: Vec<Vec<String>> =
    128         serde_json::from_str(content).map_err(|_| EventParseError::InvalidJson("content"))?;
    129     list_entries_from_tags(&tags)
    130 }