lib

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

encode.rs (3090B)


      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::EventEncodeError;
     12 use radroots_event::wire::Nip01EventWireParts;
     13 
     14 fn entry_tag(entry: &ListEntry) -> Result<Vec<String>, EventEncodeError> {
     15     if entry.tag.trim().is_empty() {
     16         return Err(EventEncodeError::EmptyRequiredField("entry.tag"));
     17     }
     18     let first = entry
     19         .values
     20         .first()
     21         .ok_or(EventEncodeError::EmptyRequiredField("entry.values"))?;
     22     if first.trim().is_empty() {
     23         return Err(EventEncodeError::EmptyRequiredField("entry.values"));
     24     }
     25     let mut tag = Vec::with_capacity(1 + entry.values.len());
     26     tag.push(entry.tag.clone());
     27     tag.extend(entry.values.iter().cloned());
     28     Ok(tag)
     29 }
     30 
     31 pub fn list_entries_to_tags(entries: &[ListEntry]) -> Result<Vec<Vec<String>>, EventEncodeError> {
     32     let mut tags = Vec::with_capacity(entries.len());
     33     for entry in entries {
     34         tags.push(entry_tag(entry)?);
     35     }
     36     Ok(tags)
     37 }
     38 
     39 pub fn list_build_tags(list: &List) -> Result<Vec<Vec<String>>, EventEncodeError> {
     40     list_entries_to_tags(&list.entries)
     41 }
     42 
     43 pub fn to_wire_parts_with_kind(
     44     list: &List,
     45     kind: u32,
     46 ) -> Result<Nip01EventWireParts, EventEncodeError> {
     47     if !is_generic_list_codec_kind(kind) {
     48         return Err(EventEncodeError::InvalidKind(kind));
     49     }
     50     if kind == KIND_LIST_READ_WRITE_RELAYS {
     51         validate_relay_entries(&list.entries)?;
     52     }
     53     let tags = list_build_tags(list)?;
     54     Ok(Nip01EventWireParts {
     55         kind,
     56         content: list.content.clone(),
     57         tags,
     58     })
     59 }
     60 
     61 fn validate_relay_entries(entries: &[ListEntry]) -> Result<(), EventEncodeError> {
     62     if entries.is_empty() {
     63         return Err(EventEncodeError::EmptyRequiredField("relay.entries"));
     64     }
     65     for entry in entries {
     66         if entry.tag != TAG_R {
     67             return Err(EventEncodeError::InvalidField("relay.tag"));
     68         }
     69         let Some(url) = entry.values.first() else {
     70             return Err(EventEncodeError::EmptyRequiredField("relay.url"));
     71         };
     72         if !is_ws_relay_url(url) {
     73             return Err(EventEncodeError::InvalidField("relay.url"));
     74         }
     75         if entry.values.len() > 2 {
     76             return Err(EventEncodeError::InvalidField("relay.marker"));
     77         }
     78         if let Some(marker) = entry.values.get(1)
     79             && marker != "read"
     80             && marker != "write"
     81         {
     82             return Err(EventEncodeError::InvalidField("relay.marker"));
     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 #[cfg(feature = "json")]
     94 pub fn list_private_entries_json(entries: &[ListEntry]) -> Result<String, EventEncodeError> {
     95     let tags = list_entries_to_tags(entries)?;
     96     serde_json::to_string(&tags).map_err(|_| EventEncodeError::Json)
     97 }