decode.rs (10829B)
1 #[cfg(not(feature = "std"))] 2 use alloc::{ 3 string::{String, ToString}, 4 vec::Vec, 5 }; 6 7 use radroots_events::{ 8 group::{ 9 KIND_GROUP_ADMINS, KIND_GROUP_CREATE_GROUP, KIND_GROUP_CREATE_INVITE, 10 KIND_GROUP_DELETE_EVENT, KIND_GROUP_DELETE_GROUP, KIND_GROUP_EDIT_METADATA, 11 KIND_GROUP_JOIN_REQUEST, KIND_GROUP_LEAVE_REQUEST, KIND_GROUP_MEMBERS, KIND_GROUP_METADATA, 12 KIND_GROUP_PUT_USER, KIND_GROUP_REMOVE_USER, KIND_GROUP_ROLES, RadrootsGroupAdmins, 13 RadrootsGroupCreateGroup, RadrootsGroupCreateInvite, RadrootsGroupDeleteEvent, 14 RadrootsGroupDeleteGroup, RadrootsGroupEditMetadata, RadrootsGroupEditableMetadata, 15 RadrootsGroupJoinRequest, RadrootsGroupLeaveRequest, RadrootsGroupMembers, 16 RadrootsGroupMetadata, RadrootsGroupPutUser, RadrootsGroupRemoveUser, RadrootsGroupRole, 17 RadrootsGroupRoles, RadrootsGroupUserRef, 18 }, 19 tags::{TAG_D, TAG_E, TAG_H, TAG_P}, 20 }; 21 22 use crate::error::EventParseError; 23 use crate::field_helpers::{ 24 optional_tag_value, require_empty_content, required_tag_value, validate_non_empty_tag_value, 25 }; 26 27 const TAG_ABOUT: &str = "about"; 28 const TAG_CLOSED: &str = "closed"; 29 const TAG_CODE: &str = "code"; 30 const TAG_HIDDEN: &str = "hidden"; 31 const TAG_NAME: &str = "name"; 32 const TAG_PICTURE: &str = "picture"; 33 const TAG_PRIVATE: &str = "private"; 34 const TAG_RESTRICTED: &str = "restricted"; 35 const TAG_ROLE: &str = "role"; 36 const TAG_SUPPORTED_KINDS: &str = "supported_kinds"; 37 38 pub fn group_put_user_from_event( 39 kind: u32, 40 tags: &[Vec<String>], 41 content: &str, 42 ) -> Result<RadrootsGroupPutUser, EventParseError> { 43 require_kind(kind, KIND_GROUP_PUT_USER, "9000")?; 44 let (pubkey, roles) = required_user_tag(tags)?; 45 Ok(RadrootsGroupPutUser { 46 group_id: required_tag_value(tags, TAG_H)?, 47 message: optional_content(content), 48 pubkey, 49 roles, 50 }) 51 } 52 53 pub fn group_remove_user_from_event( 54 kind: u32, 55 tags: &[Vec<String>], 56 content: &str, 57 ) -> Result<RadrootsGroupRemoveUser, EventParseError> { 58 require_kind(kind, KIND_GROUP_REMOVE_USER, "9001")?; 59 let (pubkey, _) = required_user_tag(tags)?; 60 Ok(RadrootsGroupRemoveUser { 61 group_id: required_tag_value(tags, TAG_H)?, 62 message: optional_content(content), 63 pubkey, 64 }) 65 } 66 67 pub fn group_create_group_from_event( 68 kind: u32, 69 tags: &[Vec<String>], 70 content: &str, 71 ) -> Result<RadrootsGroupCreateGroup, EventParseError> { 72 require_kind(kind, KIND_GROUP_CREATE_GROUP, "9007")?; 73 Ok(RadrootsGroupCreateGroup { 74 group_id: required_tag_value(tags, TAG_H)?, 75 message: optional_content(content), 76 metadata: metadata_from_tags(tags)?, 77 }) 78 } 79 80 pub fn group_edit_metadata_from_event( 81 kind: u32, 82 tags: &[Vec<String>], 83 content: &str, 84 ) -> Result<RadrootsGroupEditMetadata, EventParseError> { 85 require_kind(kind, KIND_GROUP_EDIT_METADATA, "9002")?; 86 Ok(RadrootsGroupEditMetadata { 87 group_id: required_tag_value(tags, TAG_H)?, 88 message: optional_content(content), 89 metadata: metadata_from_tags(tags)?, 90 }) 91 } 92 93 pub fn group_delete_group_from_event( 94 kind: u32, 95 tags: &[Vec<String>], 96 content: &str, 97 ) -> Result<RadrootsGroupDeleteGroup, EventParseError> { 98 require_kind(kind, KIND_GROUP_DELETE_GROUP, "9008")?; 99 Ok(RadrootsGroupDeleteGroup { 100 group_id: required_tag_value(tags, TAG_H)?, 101 message: optional_content(content), 102 }) 103 } 104 105 pub fn group_delete_event_from_event( 106 kind: u32, 107 tags: &[Vec<String>], 108 content: &str, 109 ) -> Result<RadrootsGroupDeleteEvent, EventParseError> { 110 require_kind(kind, KIND_GROUP_DELETE_EVENT, "9005")?; 111 Ok(RadrootsGroupDeleteEvent { 112 group_id: required_tag_value(tags, TAG_H)?, 113 message: optional_content(content), 114 event_id: required_tag_value(tags, TAG_E)?, 115 }) 116 } 117 118 pub fn group_create_invite_from_event( 119 kind: u32, 120 tags: &[Vec<String>], 121 content: &str, 122 ) -> Result<RadrootsGroupCreateInvite, EventParseError> { 123 require_kind(kind, KIND_GROUP_CREATE_INVITE, "9009")?; 124 Ok(RadrootsGroupCreateInvite { 125 group_id: required_tag_value(tags, TAG_H)?, 126 message: optional_content(content), 127 code: required_tag_value(tags, TAG_CODE)?, 128 }) 129 } 130 131 pub fn group_join_request_from_event( 132 kind: u32, 133 tags: &[Vec<String>], 134 content: &str, 135 ) -> Result<RadrootsGroupJoinRequest, EventParseError> { 136 require_kind(kind, KIND_GROUP_JOIN_REQUEST, "9021")?; 137 Ok(RadrootsGroupJoinRequest { 138 group_id: required_tag_value(tags, TAG_H)?, 139 message: optional_content(content), 140 code: optional_tag_value(tags, TAG_CODE)?, 141 }) 142 } 143 144 pub fn group_leave_request_from_event( 145 kind: u32, 146 tags: &[Vec<String>], 147 content: &str, 148 ) -> Result<RadrootsGroupLeaveRequest, EventParseError> { 149 require_kind(kind, KIND_GROUP_LEAVE_REQUEST, "9022")?; 150 Ok(RadrootsGroupLeaveRequest { 151 group_id: required_tag_value(tags, TAG_H)?, 152 message: optional_content(content), 153 }) 154 } 155 156 pub fn group_metadata_from_event( 157 kind: u32, 158 tags: &[Vec<String>], 159 content: &str, 160 ) -> Result<RadrootsGroupMetadata, EventParseError> { 161 require_kind(kind, KIND_GROUP_METADATA, "39000")?; 162 require_empty_content(content, "content")?; 163 Ok(RadrootsGroupMetadata { 164 d_tag: required_tag_value(tags, TAG_D)?, 165 metadata: metadata_from_tags(tags)?, 166 }) 167 } 168 169 pub fn group_admins_from_event( 170 kind: u32, 171 tags: &[Vec<String>], 172 content: &str, 173 ) -> Result<RadrootsGroupAdmins, EventParseError> { 174 require_kind(kind, KIND_GROUP_ADMINS, "39001")?; 175 Ok(RadrootsGroupAdmins { 176 d_tag: required_tag_value(tags, TAG_D)?, 177 description: optional_content(content), 178 admins: user_refs_from_tags(tags)?, 179 }) 180 } 181 182 pub fn group_members_from_event( 183 kind: u32, 184 tags: &[Vec<String>], 185 content: &str, 186 ) -> Result<RadrootsGroupMembers, EventParseError> { 187 require_kind(kind, KIND_GROUP_MEMBERS, "39002")?; 188 Ok(RadrootsGroupMembers { 189 d_tag: required_tag_value(tags, TAG_D)?, 190 description: optional_content(content), 191 members: user_refs_from_tags(tags)?, 192 }) 193 } 194 195 pub fn group_roles_from_event( 196 kind: u32, 197 tags: &[Vec<String>], 198 content: &str, 199 ) -> Result<RadrootsGroupRoles, EventParseError> { 200 require_kind(kind, KIND_GROUP_ROLES, "39003")?; 201 Ok(RadrootsGroupRoles { 202 d_tag: required_tag_value(tags, TAG_D)?, 203 description: optional_content(content), 204 roles: roles_from_tags(tags)?, 205 }) 206 } 207 208 fn require_kind( 209 kind: u32, 210 expected_kind: u32, 211 expected: &'static str, 212 ) -> Result<(), EventParseError> { 213 if kind == expected_kind { 214 Ok(()) 215 } else { 216 Err(EventParseError::InvalidKind { 217 expected, 218 got: kind, 219 }) 220 } 221 } 222 223 fn metadata_from_tags( 224 tags: &[Vec<String>], 225 ) -> Result<RadrootsGroupEditableMetadata, EventParseError> { 226 Ok(RadrootsGroupEditableMetadata { 227 name: optional_tag_value(tags, TAG_NAME)?, 228 about: optional_tag_value(tags, TAG_ABOUT)?, 229 picture: optional_tag_value(tags, TAG_PICTURE)?, 230 is_private: marker_tag(tags, TAG_PRIVATE)?, 231 is_restricted: marker_tag(tags, TAG_RESTRICTED)?, 232 is_closed: marker_tag(tags, TAG_CLOSED)?, 233 is_hidden: marker_tag(tags, TAG_HIDDEN)?, 234 supported_kinds: supported_kinds_from_tags(tags)?, 235 }) 236 } 237 238 fn marker_tag(tags: &[Vec<String>], key: &'static str) -> Result<bool, EventParseError> { 239 let mut found = false; 240 for tag in tags 241 .iter() 242 .filter(|tag| tag.first().map(|value| value.as_str()) == Some(key)) 243 { 244 if found || tag.len() != 1 { 245 return Err(EventParseError::InvalidTag(key)); 246 } 247 found = true; 248 } 249 Ok(found) 250 } 251 252 fn supported_kinds_from_tags(tags: &[Vec<String>]) -> Result<Option<Vec<u32>>, EventParseError> { 253 let mut matches = tags 254 .iter() 255 .filter(|tag| tag.first().map(|value| value.as_str()) == Some(TAG_SUPPORTED_KINDS)); 256 let Some(tag) = matches.next() else { 257 return Ok(None); 258 }; 259 if matches.next().is_some() { 260 return Err(EventParseError::InvalidTag(TAG_SUPPORTED_KINDS)); 261 } 262 let mut supported_kinds = Vec::new(); 263 for value in tag.iter().skip(1) { 264 validate_non_empty_tag_value(value, TAG_SUPPORTED_KINDS)?; 265 supported_kinds.push( 266 value 267 .parse::<u32>() 268 .map_err(|err| EventParseError::InvalidNumber(TAG_SUPPORTED_KINDS, err))?, 269 ); 270 } 271 Ok(Some(supported_kinds)) 272 } 273 274 fn required_user_tag(tags: &[Vec<String>]) -> Result<(String, Vec<String>), EventParseError> { 275 let tag = tags 276 .iter() 277 .find(|tag| tag.first().map(|value| value.as_str()) == Some(TAG_P)) 278 .ok_or(EventParseError::MissingTag(TAG_P))?; 279 user_from_tag(tag) 280 } 281 282 fn user_refs_from_tags(tags: &[Vec<String>]) -> Result<Vec<RadrootsGroupUserRef>, EventParseError> { 283 tags.iter() 284 .filter(|tag| tag.first().map(|value| value.as_str()) == Some(TAG_P)) 285 .map(|tag| { 286 let (pubkey, roles) = user_from_tag(tag)?; 287 Ok(RadrootsGroupUserRef { pubkey, roles }) 288 }) 289 .collect() 290 } 291 292 fn user_from_tag(tag: &[String]) -> Result<(String, Vec<String>), EventParseError> { 293 let pubkey = tag 294 .get(1) 295 .cloned() 296 .ok_or(EventParseError::InvalidTag(TAG_P))?; 297 validate_non_empty_tag_value(&pubkey, TAG_P)?; 298 let mut roles = Vec::new(); 299 for role in tag.iter().skip(2) { 300 validate_non_empty_tag_value(role, TAG_P)?; 301 roles.push(role.clone()); 302 } 303 Ok((pubkey, roles)) 304 } 305 306 fn roles_from_tags(tags: &[Vec<String>]) -> Result<Vec<RadrootsGroupRole>, EventParseError> { 307 tags.iter() 308 .filter(|tag| tag.first().map(|value| value.as_str()) == Some(TAG_ROLE)) 309 .map(|tag| { 310 let name = tag 311 .get(1) 312 .cloned() 313 .ok_or(EventParseError::InvalidTag(TAG_ROLE))?; 314 validate_non_empty_tag_value(&name, TAG_ROLE)?; 315 let description = tag.get(2).cloned(); 316 if let Some(description) = description.as_deref() { 317 validate_non_empty_tag_value(description, TAG_ROLE)?; 318 } 319 let mut permissions = Vec::new(); 320 for permission in tag.iter().skip(3) { 321 validate_non_empty_tag_value(permission, TAG_ROLE)?; 322 permissions.push(permission.clone()); 323 } 324 Ok(RadrootsGroupRole { 325 name, 326 description, 327 permissions, 328 }) 329 }) 330 .collect() 331 } 332 333 fn optional_content(content: &str) -> Option<String> { 334 if content.is_empty() { 335 None 336 } else { 337 Some(content.to_string()) 338 } 339 }