decode.rs (5905B)
1 #![cfg(feature = "serde_json")] 2 #![forbid(unsafe_code)] 3 4 #[cfg(not(feature = "std"))] 5 use alloc::{ 6 string::{String, ToString}, 7 vec::Vec, 8 }; 9 10 use radroots_events::{ 11 RadrootsNostrEvent, document::RadrootsDocument, kinds::KIND_DOCUMENT, tags::TAG_D, 12 }; 13 14 use crate::d_tag::validate_d_tag_tag; 15 use crate::error::EventParseError; 16 use crate::parsed::{RadrootsParsedData, RadrootsParsedEvent}; 17 18 const TAG_A: &str = "a"; 19 const TAG_P: &str = "p"; 20 const DEFAULT_KIND: u32 = KIND_DOCUMENT; 21 22 fn parse_d_tag(tags: &[Vec<String>]) -> Result<String, EventParseError> { 23 let tag = tags 24 .iter() 25 .find(|t| t.first().map(|s| s.as_str()) == Some(TAG_D)) 26 .ok_or(EventParseError::MissingTag(TAG_D))?; 27 let value = tag 28 .get(1) 29 .map(|s| s.to_string()) 30 .ok_or(EventParseError::InvalidTag(TAG_D))?; 31 if value.trim().is_empty() { 32 return Err(EventParseError::InvalidTag(TAG_D)); 33 } 34 validate_d_tag_tag(&value, TAG_D)?; 35 Ok(value) 36 } 37 38 fn parse_subject_pubkey(tags: &[Vec<String>]) -> Result<String, EventParseError> { 39 let tag = tags 40 .iter() 41 .find(|t| t.first().map(|s| s.as_str()) == Some(TAG_P)) 42 .ok_or(EventParseError::MissingTag(TAG_P))?; 43 let value = tag 44 .get(1) 45 .map(|s| s.to_string()) 46 .ok_or(EventParseError::InvalidTag(TAG_P))?; 47 if value.trim().is_empty() { 48 return Err(EventParseError::InvalidTag(TAG_P)); 49 } 50 Ok(value) 51 } 52 53 fn parse_subject_address(tags: &[Vec<String>]) -> Result<Option<String>, EventParseError> { 54 let tag = tags 55 .iter() 56 .find(|t| t.first().map(|s| s.as_str()) == Some(TAG_A)); 57 let Some(tag) = tag else { return Ok(None) }; 58 let value = tag 59 .get(1) 60 .map(|s| s.to_string()) 61 .ok_or(EventParseError::InvalidTag(TAG_A))?; 62 if value.trim().is_empty() { 63 return Err(EventParseError::InvalidTag(TAG_A)); 64 } 65 Ok(Some(value)) 66 } 67 68 pub fn document_from_event( 69 kind: u32, 70 tags: &[Vec<String>], 71 content: &str, 72 ) -> Result<RadrootsDocument, EventParseError> { 73 if kind != DEFAULT_KIND { 74 return Err(EventParseError::InvalidKind { 75 expected: "30361", 76 got: kind, 77 }); 78 } 79 if content.trim().is_empty() { 80 return Err(EventParseError::InvalidJson("content")); 81 } 82 let d_tag = parse_d_tag(tags)?; 83 let subject_pubkey = parse_subject_pubkey(tags)?; 84 let subject_address = parse_subject_address(tags)?; 85 let mut document: RadrootsDocument = 86 serde_json::from_str(content).map_err(|_| EventParseError::InvalidJson("content"))?; 87 88 if document.d_tag.trim().is_empty() { 89 document.d_tag = d_tag; 90 } else if document.d_tag != d_tag { 91 return Err(EventParseError::InvalidTag(TAG_D)); 92 } 93 94 if document.subject.pubkey.trim().is_empty() { 95 document.subject.pubkey = subject_pubkey; 96 } else if document.subject.pubkey != subject_pubkey { 97 return Err(EventParseError::InvalidTag(TAG_P)); 98 } 99 100 if let Some(address) = document.subject.address.as_ref() 101 && address.trim().is_empty() 102 { 103 return Err(EventParseError::InvalidTag(TAG_A)); 104 } 105 106 if let Some(tag_address) = subject_address { 107 match document.subject.address.as_ref() { 108 None => { 109 document.subject.address = Some(tag_address); 110 } 111 Some(existing) => { 112 if existing != &tag_address { 113 return Err(EventParseError::InvalidTag(TAG_A)); 114 } 115 } 116 } 117 } else if document.subject.address.is_some() { 118 return Err(EventParseError::MissingTag(TAG_A)); 119 } 120 121 Ok(document) 122 } 123 124 pub fn data_from_event( 125 id: String, 126 author: String, 127 published_at: u32, 128 kind: u32, 129 content: String, 130 tags: Vec<Vec<String>>, 131 ) -> Result<RadrootsParsedData<RadrootsDocument>, EventParseError> { 132 let document = document_from_event(kind, &tags, &content)?; 133 Ok(RadrootsParsedData::new( 134 id, 135 author, 136 published_at, 137 kind, 138 document, 139 )) 140 } 141 142 pub fn parsed_from_event( 143 id: String, 144 author: String, 145 published_at: u32, 146 kind: u32, 147 content: String, 148 tags: Vec<Vec<String>>, 149 sig: String, 150 ) -> Result<RadrootsParsedEvent<RadrootsDocument>, EventParseError> { 151 let data = data_from_event( 152 id.clone(), 153 author.clone(), 154 published_at, 155 kind, 156 content.clone(), 157 tags.clone(), 158 )?; 159 Ok(RadrootsParsedEvent { 160 event: RadrootsNostrEvent { 161 id, 162 author, 163 created_at: published_at, 164 kind, 165 content, 166 tags, 167 sig, 168 }, 169 data, 170 }) 171 } 172 173 #[cfg(test)] 174 mod tests { 175 use super::*; 176 use radroots_events::document::{RadrootsDocument, RadrootsDocumentSubject}; 177 178 #[test] 179 fn document_decode_accepts_subject_without_address() { 180 let document = RadrootsDocument { 181 d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(), 182 doc_type: "policy".to_string(), 183 title: "Farm policy".to_string(), 184 version: "1.0.0".to_string(), 185 summary: None, 186 effective_at: None, 187 body_markdown: None, 188 subject: RadrootsDocumentSubject { 189 pubkey: "subject-pubkey".to_string(), 190 address: None, 191 }, 192 tags: None, 193 }; 194 let content = serde_json::to_string(&document).expect("document content"); 195 196 let decoded = document_from_event( 197 DEFAULT_KIND, 198 &[ 199 vec![TAG_D.to_string(), "AAAAAAAAAAAAAAAAAAAAAA".to_string()], 200 vec![TAG_P.to_string(), "subject-pubkey".to_string()], 201 ], 202 &content, 203 ) 204 .expect("document"); 205 206 assert_eq!(decoded.subject.address, None); 207 } 208 }