decode.rs (3584B)
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 super::RadrootsProfileData; 10 use radroots_events::{ 11 RadrootsNostrEvent, 12 kinds::KIND_PROFILE, 13 profile::{ 14 RADROOTS_PROFILE_TYPE_TAG_KEY, RadrootsProfile, RadrootsProfileType, 15 radroots_profile_type_from_tag_value, 16 }, 17 }; 18 19 use crate::error::EventParseError; 20 use crate::parsed::{RadrootsParsedData, RadrootsParsedEvent}; 21 use serde_json::Value; 22 23 const PROFILE_KIND: u32 = KIND_PROFILE; 24 25 fn parse_optional_string(value: &Value, key: &'static str) -> Option<String> { 26 value 27 .get(key) 28 .and_then(|v| v.as_str()) 29 .map(|s| s.to_string()) 30 } 31 32 fn parse_bot(value: &Value) -> Option<String> { 33 match value.get("bot") { 34 Some(v) if v.is_string() => v.as_str().map(|s| s.to_string()), 35 Some(v) if v.is_boolean() => v.as_bool().map(|b| b.to_string()), 36 _ => None, 37 } 38 } 39 40 fn profile_type_from_tags(tags: &[Vec<String>]) -> Option<RadrootsProfileType> { 41 tags.iter() 42 .filter(|tag| tag.first().map(|v| v.as_str()) == Some(RADROOTS_PROFILE_TYPE_TAG_KEY)) 43 .filter_map(|tag| tag.get(1)) 44 .find_map(|value| radroots_profile_type_from_tag_value(value)) 45 } 46 47 pub fn profile_from_content(content: &str) -> Result<RadrootsProfile, EventParseError> { 48 let value: Value = 49 serde_json::from_str(content).map_err(|_| EventParseError::InvalidJson("content"))?; 50 let obj = value 51 .as_object() 52 .ok_or(EventParseError::InvalidJson("content"))?; 53 let name = obj 54 .get("name") 55 .and_then(|v| v.as_str()) 56 .ok_or(EventParseError::InvalidJson("name"))?; 57 58 Ok(RadrootsProfile { 59 name: name.to_string(), 60 display_name: parse_optional_string(&value, "display_name"), 61 nip05: parse_optional_string(&value, "nip05"), 62 about: parse_optional_string(&value, "about"), 63 website: parse_optional_string(&value, "website"), 64 picture: parse_optional_string(&value, "picture"), 65 banner: parse_optional_string(&value, "banner"), 66 lud06: parse_optional_string(&value, "lud06"), 67 lud16: parse_optional_string(&value, "lud16"), 68 bot: parse_bot(&value), 69 }) 70 } 71 72 pub fn data_from_event( 73 id: String, 74 author: String, 75 published_at: u32, 76 kind: u32, 77 content: String, 78 tags: Vec<Vec<String>>, 79 ) -> Result<RadrootsParsedData<RadrootsProfileData>, EventParseError> { 80 if kind != PROFILE_KIND { 81 return Err(EventParseError::InvalidKind { 82 expected: "0", 83 got: kind, 84 }); 85 } 86 let profile = profile_from_content(&content)?; 87 let profile_type = profile_type_from_tags(&tags); 88 Ok(RadrootsParsedData::new( 89 id, 90 author, 91 published_at, 92 kind, 93 RadrootsProfileData { 94 profile_type, 95 profile, 96 }, 97 )) 98 } 99 100 pub fn parsed_from_event( 101 id: String, 102 author: String, 103 published_at: u32, 104 kind: u32, 105 content: String, 106 tags: Vec<Vec<String>>, 107 sig: String, 108 ) -> Result<RadrootsParsedEvent<RadrootsProfileData>, EventParseError> { 109 let data = data_from_event( 110 id.clone(), 111 author.clone(), 112 published_at, 113 kind, 114 content.clone(), 115 tags.clone(), 116 )?; 117 Ok(RadrootsParsedEvent { 118 event: RadrootsNostrEvent { 119 id, 120 author, 121 created_at: published_at, 122 kind, 123 content, 124 tags, 125 sig, 126 }, 127 data, 128 }) 129 }