decode.rs (5327B)
1 #![cfg(feature = "json")] 2 3 #[cfg(not(feature = "std"))] 4 use alloc::{ 5 string::{String, ToString}, 6 vec::Vec, 7 }; 8 9 use radroots_event::{ 10 envelope::kind::KIND_FARM, 11 farm::Farm, 12 farm::location::{has_textual_locality, is_public_geohash5}, 13 tag::name::TAG_D, 14 }; 15 16 use crate::d_tag::validate_d_tag_tag; 17 use crate::error::EventParseError; 18 use crate::parsed::{RadrootsParsedData, RadrootsParsedEvent}; 19 20 const DEFAULT_KIND: u32 = KIND_FARM; 21 const TAG_G: &str = "g"; 22 23 fn parse_d_tag(tags: &[Vec<String>]) -> Result<String, EventParseError> { 24 let tag = tags 25 .iter() 26 .find(|t| t.first().map(|s| s.as_str()) == Some(TAG_D)) 27 .ok_or(EventParseError::MissingTag(TAG_D))?; 28 let value = tag 29 .get(1) 30 .map(|s| s.to_string()) 31 .ok_or(EventParseError::InvalidTag(TAG_D))?; 32 if value.trim().is_empty() { 33 return Err(EventParseError::InvalidTag(TAG_D)); 34 } 35 validate_d_tag_tag(&value, TAG_D)?; 36 Ok(value) 37 } 38 39 pub fn farm_from_event( 40 kind: u32, 41 tags: &[Vec<String>], 42 content: &str, 43 ) -> Result<Farm, EventParseError> { 44 if kind != DEFAULT_KIND { 45 return Err(EventParseError::InvalidKind { 46 expected: "30340", 47 got: kind, 48 }); 49 } 50 if content.trim().is_empty() { 51 return Err(EventParseError::InvalidJson("content")); 52 } 53 let d_tag = parse_d_tag(tags)?; 54 reject_private_farm_location_tags(tags)?; 55 reject_private_farm_ops_content(content)?; 56 let mut farm: Farm = 57 serde_json::from_str(content).map_err(|_| EventParseError::InvalidJson("content"))?; 58 59 if farm.d_tag.trim().is_empty() { 60 farm.d_tag = d_tag; 61 } else if farm.d_tag != d_tag { 62 return Err(EventParseError::InvalidTag(TAG_D)); 63 } 64 if let Some(location) = farm.location.as_ref() 65 && (!is_public_geohash5(&location.geohash) 66 || !has_textual_locality( 67 &location.primary, 68 location.city.as_deref(), 69 location.region.as_deref(), 70 location.country.as_deref(), 71 )) 72 { 73 return Err(EventParseError::InvalidTag(TAG_G)); 74 } 75 76 Ok(farm) 77 } 78 79 fn reject_private_farm_location_tags(tags: &[Vec<String>]) -> Result<(), EventParseError> { 80 for tag in tags { 81 let Some(key) = tag.first().map(|value| value.as_str()) else { 82 continue; 83 }; 84 match key { 85 TAG_G => { 86 let Some(value) = tag.get(1).map(|value| value.trim()) else { 87 return Err(EventParseError::InvalidTag(TAG_G)); 88 }; 89 if !is_public_geohash5(value) { 90 return Err(EventParseError::InvalidTag(TAG_G)); 91 } 92 } 93 "dd" => return Err(EventParseError::InvalidTag("dd")), 94 "dd.lat" => return Err(EventParseError::InvalidTag("dd.lat")), 95 "dd.lon" => return Err(EventParseError::InvalidTag("dd.lon")), 96 "l" => return Err(EventParseError::InvalidTag("l")), 97 "L" => return Err(EventParseError::InvalidTag("L")), 98 _ => {} 99 } 100 } 101 Ok(()) 102 } 103 104 fn reject_private_farm_ops_content(content: &str) -> Result<(), EventParseError> { 105 let value: serde_json::Value = 106 serde_json::from_str(content).map_err(|_| EventParseError::InvalidJson("content"))?; 107 let Some(object) = value.as_object() else { 108 return Err(EventParseError::InvalidJson("content")); 109 }; 110 for key in [ 111 "workspace", 112 "farm_group_id", 113 "document_id", 114 "document_kind", 115 "crdt_backend", 116 "encoded_change", 117 "semantic_kind", 118 "owner_document_kind", 119 "owner_document_id", 120 "relays", 121 "media_servers", 122 "supported_kinds", 123 "protocol_version", 124 ] { 125 if object.contains_key(key) { 126 return Err(EventParseError::InvalidJson("content")); 127 } 128 } 129 if let Some(location) = object.get("location").and_then(|value| value.as_object()) { 130 for key in [ 131 "gcs", 132 "lat", 133 "lng", 134 "lon", 135 "point", 136 "polygon", 137 "coordinates", 138 "accuracy", 139 "altitude", 140 "label", 141 "tag_0", 142 ] { 143 if location.contains_key(key) { 144 return Err(EventParseError::InvalidJson("content")); 145 } 146 } 147 } 148 Ok(()) 149 } 150 151 pub fn data_from_event( 152 id: String, 153 author: String, 154 published_at: u64, 155 kind: u32, 156 content: String, 157 tags: Vec<Vec<String>>, 158 ) -> Result<RadrootsParsedData<Farm>, EventParseError> { 159 let farm = farm_from_event(kind, &tags, &content)?; 160 Ok(RadrootsParsedData::new( 161 id, 162 author, 163 published_at, 164 kind, 165 farm, 166 )) 167 } 168 169 pub fn parsed_from_event( 170 id: String, 171 author: String, 172 published_at: u64, 173 kind: u32, 174 content: String, 175 tags: Vec<Vec<String>>, 176 sig: String, 177 ) -> Result<RadrootsParsedEvent<Farm>, EventParseError> { 178 let data = data_from_event( 179 id.clone(), 180 author.clone(), 181 published_at, 182 kind, 183 content.clone(), 184 tags.clone(), 185 )?; 186 RadrootsParsedEvent::from_event_parts(id, author, published_at, kind, content, tags, sig, data) 187 }