decode.rs (5536B)
1 #![cfg(feature = "serde_json")] 2 3 #[cfg(not(feature = "std"))] 4 use alloc::{ 5 string::{String, ToString}, 6 vec::Vec, 7 }; 8 9 use radroots_events::{ 10 RadrootsNostrEvent, 11 farm_crdt::{KIND_FARM_CRDT_CHANGE, RADROOTS_FARM_CRDT_TAG, RadrootsFarmCrdtChange}, 12 farm_workspace::KIND_FARM_WORKSPACE_MANIFEST, 13 tags::{TAG_A, TAG_D, TAG_H, TAG_P, TAG_T}, 14 }; 15 16 use crate::d_tag::validate_d_tag_tag; 17 use crate::error::EventParseError; 18 use crate::farm_crdt::encode::validate_change; 19 use crate::field_helpers::{ 20 optional_tag_value, parse_address_tag_with_kind, required_tag_value, tag_values, 21 validate_non_empty_tag_value, 22 }; 23 use crate::parsed::{RadrootsParsedData, RadrootsParsedEvent}; 24 25 const EXPECTED_KIND: &str = "78"; 26 27 pub fn farm_crdt_change_from_event( 28 kind: u32, 29 tags: &[Vec<String>], 30 content: &str, 31 ) -> Result<RadrootsFarmCrdtChange, EventParseError> { 32 farm_crdt_change_from_event_inner(kind, tags, content, None) 33 } 34 35 pub fn farm_crdt_change_from_event_with_author( 36 kind: u32, 37 tags: &[Vec<String>], 38 content: &str, 39 author_pubkey: &str, 40 ) -> Result<RadrootsFarmCrdtChange, EventParseError> { 41 validate_non_empty_tag_value(author_pubkey, TAG_P)?; 42 farm_crdt_change_from_event_inner(kind, tags, content, Some(author_pubkey)) 43 } 44 45 pub fn data_from_event( 46 id: String, 47 author: String, 48 published_at: u32, 49 kind: u32, 50 content: String, 51 tags: Vec<Vec<String>>, 52 ) -> Result<RadrootsParsedData<RadrootsFarmCrdtChange>, EventParseError> { 53 let change = farm_crdt_change_from_event_with_author(kind, &tags, &content, &author)?; 54 Ok(RadrootsParsedData::new( 55 id, 56 author, 57 published_at, 58 kind, 59 change, 60 )) 61 } 62 63 pub fn parsed_from_event( 64 id: String, 65 author: String, 66 published_at: u32, 67 kind: u32, 68 content: String, 69 tags: Vec<Vec<String>>, 70 sig: String, 71 ) -> Result<RadrootsParsedEvent<RadrootsFarmCrdtChange>, EventParseError> { 72 let data = data_from_event( 73 id.clone(), 74 author.clone(), 75 published_at, 76 kind, 77 content.clone(), 78 tags.clone(), 79 )?; 80 Ok(RadrootsParsedEvent { 81 event: RadrootsNostrEvent { 82 id, 83 author, 84 created_at: published_at, 85 kind, 86 content, 87 tags, 88 sig, 89 }, 90 data, 91 }) 92 } 93 94 fn farm_crdt_change_from_event_inner( 95 kind: u32, 96 tags: &[Vec<String>], 97 content: &str, 98 author_pubkey: Option<&str>, 99 ) -> Result<RadrootsFarmCrdtChange, EventParseError> { 100 if kind != KIND_FARM_CRDT_CHANGE { 101 return Err(EventParseError::InvalidKind { 102 expected: EXPECTED_KIND, 103 got: kind, 104 }); 105 } 106 if content.trim().is_empty() { 107 return Err(EventParseError::InvalidJson("content")); 108 } 109 110 let farm_group_id = required_tag_value(tags, TAG_H)?; 111 let document_id = required_tag_value(tags, TAG_D)?; 112 validate_d_tag_tag(&document_id, TAG_D)?; 113 let workspace_address = required_tag_value(tags, TAG_A)?; 114 let workspace = 115 parse_address_tag_with_kind(&workspace_address, KIND_FARM_WORKSPACE_MANIFEST, TAG_A)?; 116 let marker_tags = tag_values(tags, TAG_T)?; 117 if !marker_tags 118 .iter() 119 .any(|value| value == RADROOTS_FARM_CRDT_TAG) 120 { 121 return Err(EventParseError::MissingTag(TAG_T)); 122 } 123 if let Some(tag_author) = optional_tag_value(tags, TAG_P)? 124 && let Some(author_pubkey) = author_pubkey 125 && tag_author != author_pubkey 126 { 127 return Err(EventParseError::InvalidTag(TAG_P)); 128 } 129 130 let change: RadrootsFarmCrdtChange = 131 serde_json::from_str(content).map_err(|_| EventParseError::InvalidJson("content"))?; 132 validate_change(&change).map_err(encode_error_to_parse_error)?; 133 if change.farm_group_id != farm_group_id { 134 return Err(EventParseError::InvalidTag(TAG_H)); 135 } 136 if change.document_id != document_id { 137 return Err(EventParseError::InvalidTag(TAG_D)); 138 } 139 if change.workspace.pubkey != workspace.pubkey || change.workspace.d_tag != workspace.d_tag { 140 return Err(EventParseError::InvalidTag(TAG_A)); 141 } 142 Ok(change) 143 } 144 145 fn encode_error_to_parse_error(error: crate::error::EventEncodeError) -> EventParseError { 146 match error { 147 crate::error::EventEncodeError::InvalidKind(kind) => EventParseError::InvalidKind { 148 expected: EXPECTED_KIND, 149 got: kind, 150 }, 151 crate::error::EventEncodeError::EmptyRequiredField(field) 152 | crate::error::EventEncodeError::InvalidField(field) => match field { 153 "farm_group_id" => EventParseError::InvalidTag(TAG_H), 154 "document_id" => EventParseError::InvalidTag(TAG_D), 155 "workspace.pubkey" | "workspace.d_tag" => EventParseError::InvalidTag(TAG_A), 156 _ => EventParseError::InvalidJson(field), 157 }, 158 crate::error::EventEncodeError::Json => EventParseError::InvalidJson("content"), 159 } 160 } 161 162 #[cfg(test)] 163 mod tests { 164 use super::*; 165 use crate::error::EventEncodeError; 166 167 #[test] 168 fn encode_error_mapper_covers_kind_and_json_edges() { 169 assert!(matches!( 170 encode_error_to_parse_error(EventEncodeError::InvalidKind(1)), 171 EventParseError::InvalidKind { 172 expected: EXPECTED_KIND, 173 got: 1 174 } 175 )); 176 assert!(matches!( 177 encode_error_to_parse_error(EventEncodeError::Json), 178 EventParseError::InvalidJson("content") 179 )); 180 } 181 }