ruma_state_res/events/
member.rs

1//! Types to deserialize `m.room.member` events.
2
3use std::ops::Deref;
4
5use ruma_common::{serde::from_raw_json_value, CanonicalJsonObject, OwnedUserId};
6use ruma_events::room::member::MembershipState;
7use ruma_signatures::canonical_json;
8use serde::Deserialize;
9use serde_json::value::RawValue as RawJsonValue;
10
11use super::Event;
12
13/// A helper type for an [`Event`] of type `m.room.member`.
14///
15/// This is a type that deserializes each field lazily, as requested.
16#[derive(Debug, Clone)]
17pub struct RoomMemberEvent<E: Event>(E);
18
19impl<E: Event> RoomMemberEvent<E> {
20    /// Construct a new `RoomMemberEvent` around the given event.
21    pub fn new(event: E) -> Self {
22        Self(event)
23    }
24
25    /// The membership of the user.
26    pub fn membership(&self) -> Result<MembershipState, String> {
27        RoomMemberEventContent(self.content()).membership()
28    }
29
30    /// If this is a `join` event, the ID of a user on the homeserver that authorized it.
31    pub fn join_authorised_via_users_server(&self) -> Result<Option<OwnedUserId>, String> {
32        RoomMemberEventContent(self.content()).join_authorised_via_users_server()
33    }
34
35    /// If this is an `invite` event, details about the third-party invite that resulted in this
36    /// event.
37    pub(crate) fn third_party_invite(&self) -> Result<Option<ThirdPartyInvite>, String> {
38        RoomMemberEventContent(self.content()).third_party_invite()
39    }
40}
41
42impl<E: Event> Deref for RoomMemberEvent<E> {
43    type Target = E;
44
45    fn deref(&self) -> &Self::Target {
46        &self.0
47    }
48}
49
50/// Helper trait for `Option<RoomMemberEvent<E>>`.
51pub(crate) trait RoomMemberEventOptionExt {
52    /// The membership of the user.
53    ///
54    /// Defaults to `leave` if there is no `m.room.member` event.
55    fn membership(&self) -> Result<MembershipState, String>;
56}
57
58impl<E: Event> RoomMemberEventOptionExt for Option<RoomMemberEvent<E>> {
59    fn membership(&self) -> Result<MembershipState, String> {
60        match self {
61            Some(event) => event.membership(),
62            None => Ok(MembershipState::Leave),
63        }
64    }
65}
66
67/// A helper type for the raw JSON content of an event of type `m.room.member`.
68pub(crate) struct RoomMemberEventContent<'a>(&'a RawJsonValue);
69
70impl<'a> RoomMemberEventContent<'a> {
71    /// Construct a new `RoomMemberEventContent` around the given raw JSON content.
72    pub(crate) fn new(content: &'a RawJsonValue) -> Self {
73        Self(content)
74    }
75}
76
77impl RoomMemberEventContent<'_> {
78    /// The membership of the user.
79    pub(crate) fn membership(&self) -> Result<MembershipState, String> {
80        #[derive(Deserialize)]
81        struct RoomMemberContentMembership {
82            membership: MembershipState,
83        }
84
85        let content: RoomMemberContentMembership =
86            from_raw_json_value(self.0).map_err(|err: serde_json::Error| {
87                format!("missing or invalid `membership` field in `m.room.member` event: {err}")
88            })?;
89        Ok(content.membership)
90    }
91
92    /// If this is a `join` event, the ID of a user on the homeserver that authorized it.
93    pub(crate) fn join_authorised_via_users_server(&self) -> Result<Option<OwnedUserId>, String> {
94        #[derive(Deserialize)]
95        struct RoomMemberContentJoinAuthorizedViaUsersServer {
96            join_authorised_via_users_server: Option<OwnedUserId>,
97        }
98
99        let content: RoomMemberContentJoinAuthorizedViaUsersServer = from_raw_json_value(self.0)
100            .map_err(|err: serde_json::Error| {
101                format!(
102                "invalid `join_authorised_via_users_server` field in `m.room.member` event: {err}"
103            )
104            })?;
105        Ok(content.join_authorised_via_users_server)
106    }
107
108    /// If this is an `invite` event, details about the third-party invite that resulted in this
109    /// event.
110    pub(crate) fn third_party_invite(&self) -> Result<Option<ThirdPartyInvite>, String> {
111        #[derive(Deserialize)]
112        struct RoomMemberContentThirdPartyInvite {
113            third_party_invite: Option<ThirdPartyInvite>,
114        }
115
116        let content: RoomMemberContentThirdPartyInvite =
117            from_raw_json_value(self.0).map_err(|err: serde_json::Error| {
118                format!("invalid `third_party_invite` field in `m.room.member` event: {err}")
119            })?;
120        Ok(content.third_party_invite)
121    }
122}
123
124/// Details about a third-party invite.
125#[derive(Deserialize)]
126pub(crate) struct ThirdPartyInvite {
127    /// Signed details about the third-party invite.
128    signed: CanonicalJsonObject,
129}
130
131impl ThirdPartyInvite {
132    /// The unique identifier for the third-party invite.
133    pub(crate) fn token(&self) -> Result<&str, String> {
134        let Some(token_value) = self.signed.get("token") else {
135            return Err("missing `token` field in `third_party_invite.signed` \
136                        of `m.room.member` event"
137                .into());
138        };
139
140        token_value.as_str().ok_or_else(|| {
141            format!(
142                "unexpected format of `token` field in `third_party_invite.signed` \
143                 of `m.room.member` event: expected string, got {token_value:?}"
144            )
145        })
146    }
147
148    /// The Matrix ID of the user that was invited.
149    pub(crate) fn mxid(&self) -> Result<&str, String> {
150        let Some(mxid_value) = self.signed.get("mxid") else {
151            return Err("missing `mxid` field in `third_party_invite.signed` \
152                        of `m.room.member` event"
153                .into());
154        };
155
156        mxid_value.as_str().ok_or_else(|| {
157            format!(
158                "unexpected format of `mxid` field in `third_party_invite.signed` \
159                 of `m.room.member` event: expected string, got {mxid_value:?}"
160            )
161        })
162    }
163
164    /// The signatures of the event.
165    pub(crate) fn signatures(&self) -> Result<&CanonicalJsonObject, String> {
166        let Some(signatures_value) = self.signed.get("signatures") else {
167            return Err("missing `signatures` field in `third_party_invite.signed` \
168                        of `m.room.member` event"
169                .into());
170        };
171
172        signatures_value.as_object().ok_or_else(|| {
173            format!(
174                "unexpected format of `signatures` field in `third_party_invite.signed` \
175                 of `m.room.member` event: expected object, got {signatures_value:?}"
176            )
177        })
178    }
179
180    /// The `signed` object as canonical JSON string to verify the signatures.
181    pub(crate) fn signed_canonical_json(&self) -> Result<String, String> {
182        canonical_json(&self.signed).map_err(|error| {
183            format!("invalid `third_party_invite.signed` field in `m.room.member` event: {error}")
184        })
185    }
186}