lib

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

encode.rs (4218B)


      1 #[cfg(not(feature = "std"))]
      2 use alloc::{
      3     format,
      4     string::{String, ToString},
      5     vec::Vec,
      6 };
      7 
      8 use radroots_events::{
      9     kinds::KIND_REACTION, reaction::RadrootsReaction, social::RadrootsSocialTarget,
     10 };
     11 
     12 use crate::error::EventEncodeError;
     13 use crate::field_helpers::{
     14     parse_address_tag, validate_lowercase_hex_64, validate_non_empty_field,
     15 };
     16 use crate::wire::WireEventParts;
     17 
     18 const DEFAULT_KIND: u32 = KIND_REACTION;
     19 
     20 pub fn reaction_build_tags(
     21     reaction: &RadrootsReaction,
     22 ) -> Result<Vec<Vec<String>>, EventEncodeError> {
     23     let mut tags = Vec::with_capacity(4);
     24     push_reaction_target(&mut tags, &reaction.target)?;
     25     Ok(tags)
     26 }
     27 
     28 pub fn to_wire_parts(reaction: &RadrootsReaction) -> Result<WireEventParts, EventEncodeError> {
     29     to_wire_parts_with_kind(reaction, DEFAULT_KIND)
     30 }
     31 
     32 pub fn to_wire_parts_with_kind(
     33     reaction: &RadrootsReaction,
     34     kind: u32,
     35 ) -> Result<WireEventParts, EventEncodeError> {
     36     if kind != DEFAULT_KIND {
     37         return Err(EventEncodeError::InvalidKind(kind));
     38     }
     39     let tags = reaction_build_tags(reaction)?;
     40     Ok(WireEventParts {
     41         kind,
     42         content: reaction.content.clone(),
     43         tags,
     44     })
     45 }
     46 
     47 fn push_reaction_target(
     48     tags: &mut Vec<Vec<String>>,
     49     target: &RadrootsSocialTarget,
     50 ) -> Result<(), EventEncodeError> {
     51     match target {
     52         RadrootsSocialTarget::Event {
     53             id,
     54             author,
     55             event_kind,
     56             relays,
     57         } => {
     58             validate_lowercase_hex_64(id, "target.id")?;
     59             let mut event_tag = Vec::with_capacity(2 + relays.as_ref().map_or(0, Vec::len));
     60             event_tag.push("e".to_string());
     61             event_tag.push(id.clone());
     62             if let Some(relays) = relays {
     63                 event_tag.extend(relays.iter().cloned());
     64             }
     65             tags.push(event_tag);
     66             if let Some(author) = author.as_deref() {
     67                 validate_non_empty_field(author, "target.author")?;
     68                 tags.push(vec!["p".to_string(), author.to_string()]);
     69             }
     70             if let Some(kind) = event_kind {
     71                 tags.push(vec!["k".to_string(), kind.to_string()]);
     72             }
     73         }
     74         RadrootsSocialTarget::Address {
     75             address,
     76             author,
     77             event_kind,
     78             relays,
     79         } => {
     80             let parsed = parse_address_tag(address, "target.address")
     81                 .map_err(|_| EventEncodeError::InvalidField("target.address"))?;
     82             if let Some(kind) = event_kind
     83                 && *kind != parsed.kind
     84             {
     85                 return Err(EventEncodeError::InvalidField("target.kind"));
     86             }
     87             if let Some(author) = author.as_deref()
     88                 && author != parsed.pubkey
     89             {
     90                 return Err(EventEncodeError::InvalidField("target.author"));
     91             }
     92             let mut address_tag = Vec::with_capacity(2 + relays.as_ref().map_or(0, Vec::len));
     93             address_tag.push("a".to_string());
     94             address_tag.push(format!(
     95                 "{}:{}:{}",
     96                 parsed.kind, parsed.pubkey, parsed.d_tag
     97             ));
     98             if let Some(relays) = relays {
     99                 address_tag.extend(relays.iter().cloned());
    100             }
    101             tags.push(address_tag);
    102             tags.push(vec!["p".to_string(), parsed.pubkey]);
    103             tags.push(vec!["k".to_string(), parsed.kind.to_string()]);
    104         }
    105         RadrootsSocialTarget::External { .. } => {
    106             return Err(EventEncodeError::InvalidField("target"));
    107         }
    108     }
    109     Ok(())
    110 }
    111 
    112 #[cfg(test)]
    113 mod tests {
    114     use super::*;
    115 
    116     #[test]
    117     fn reaction_event_target_encodes_without_relays() {
    118         let reaction = RadrootsReaction {
    119             target: RadrootsSocialTarget::Event {
    120                 id: "a".repeat(64),
    121                 author: Some("b".repeat(64)),
    122                 event_kind: Some(1),
    123                 relays: None,
    124             },
    125             content: "+".to_string(),
    126         };
    127 
    128         let tags = reaction_build_tags(&reaction).expect("reaction tags");
    129         assert!(
    130             tags.iter()
    131                 .any(|tag| tag == &vec!["e".to_string(), "a".repeat(64)])
    132         );
    133     }
    134 }