lib

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

decode.rs (4068B)


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