lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

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 }