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 }