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