1//! Types to deserialize `m.room.member` events.
23use std::ops::Deref;
45use 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;
1011use super::Event;
1213/// 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);
1819impl<E: Event> RoomMemberEvent<E> {
20/// Construct a new `RoomMemberEvent` around the given event.
21pub fn new(event: E) -> Self {
22Self(event)
23 }
2425/// The membership of the user.
26pub fn membership(&self) -> Result<MembershipState, String> {
27 RoomMemberEventContent(self.content()).membership()
28 }
2930/// If this is a `join` event, the ID of a user on the homeserver that authorized it.
31pub fn join_authorised_via_users_server(&self) -> Result<Option<OwnedUserId>, String> {
32 RoomMemberEventContent(self.content()).join_authorised_via_users_server()
33 }
3435/// If this is an `invite` event, details about the third-party invite that resulted in this
36 /// event.
37pub(crate) fn third_party_invite(&self) -> Result<Option<ThirdPartyInvite>, String> {
38 RoomMemberEventContent(self.content()).third_party_invite()
39 }
40}
4142impl<E: Event> Deref for RoomMemberEvent<E> {
43type Target = E;
4445fn deref(&self) -> &Self::Target {
46&self.0
47}
48}
4950/// 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.
55fn membership(&self) -> Result<MembershipState, String>;
56}
5758impl<E: Event> RoomMemberEventOptionExt for Option<RoomMemberEvent<E>> {
59fn membership(&self) -> Result<MembershipState, String> {
60match self {
61Some(event) => event.membership(),
62None => Ok(MembershipState::Leave),
63 }
64 }
65}
6667/// A helper type for the raw JSON content of an event of type `m.room.member`.
68pub(crate) struct RoomMemberEventContent<'a>(&'a RawJsonValue);
6970impl<'a> RoomMemberEventContent<'a> {
71/// Construct a new `RoomMemberEventContent` around the given raw JSON content.
72pub(crate) fn new(content: &'a RawJsonValue) -> Self {
73Self(content)
74 }
75}
7677impl RoomMemberEventContent<'_> {
78/// The membership of the user.
79pub(crate) fn membership(&self) -> Result<MembershipState, String> {
80#[derive(Deserialize)]
81struct RoomMemberContentMembership {
82 membership: MembershipState,
83 }
8485let content: RoomMemberContentMembership =
86 from_raw_json_value(self.0).map_err(|err: serde_json::Error| {
87format!("missing or invalid `membership` field in `m.room.member` event: {err}")
88 })?;
89Ok(content.membership)
90 }
9192/// If this is a `join` event, the ID of a user on the homeserver that authorized it.
93pub(crate) fn join_authorised_via_users_server(&self) -> Result<Option<OwnedUserId>, String> {
94#[derive(Deserialize)]
95struct RoomMemberContentJoinAuthorizedViaUsersServer {
96 join_authorised_via_users_server: Option<OwnedUserId>,
97 }
9899let content: RoomMemberContentJoinAuthorizedViaUsersServer = from_raw_json_value(self.0)
100 .map_err(|err: serde_json::Error| {
101format!(
102"invalid `join_authorised_via_users_server` field in `m.room.member` event: {err}"
103)
104 })?;
105Ok(content.join_authorised_via_users_server)
106 }
107108/// If this is an `invite` event, details about the third-party invite that resulted in this
109 /// event.
110pub(crate) fn third_party_invite(&self) -> Result<Option<ThirdPartyInvite>, String> {
111#[derive(Deserialize)]
112struct RoomMemberContentThirdPartyInvite {
113 third_party_invite: Option<ThirdPartyInvite>,
114 }
115116let content: RoomMemberContentThirdPartyInvite =
117 from_raw_json_value(self.0).map_err(|err: serde_json::Error| {
118format!("invalid `third_party_invite` field in `m.room.member` event: {err}")
119 })?;
120Ok(content.third_party_invite)
121 }
122}
123124/// Details about a third-party invite.
125#[derive(Deserialize)]
126pub(crate) struct ThirdPartyInvite {
127/// Signed details about the third-party invite.
128signed: CanonicalJsonObject,
129}
130131impl ThirdPartyInvite {
132/// The unique identifier for the third-party invite.
133pub(crate) fn token(&self) -> Result<&str, String> {
134let Some(token_value) = self.signed.get("token") else {
135return Err("missing `token` field in `third_party_invite.signed` \
136 of `m.room.member` event"
137.into());
138 };
139140 token_value.as_str().ok_or_else(|| {
141format!(
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 }
147148/// The Matrix ID of the user that was invited.
149pub(crate) fn mxid(&self) -> Result<&str, String> {
150let Some(mxid_value) = self.signed.get("mxid") else {
151return Err("missing `mxid` field in `third_party_invite.signed` \
152 of `m.room.member` event"
153.into());
154 };
155156 mxid_value.as_str().ok_or_else(|| {
157format!(
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 }
163164/// The signatures of the event.
165pub(crate) fn signatures(&self) -> Result<&CanonicalJsonObject, String> {
166let Some(signatures_value) = self.signed.get("signatures") else {
167return Err("missing `signatures` field in `third_party_invite.signed` \
168 of `m.room.member` event"
169.into());
170 };
171172 signatures_value.as_object().ok_or_else(|| {
173format!(
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 }
179180/// The `signed` object as canonical JSON string to verify the signatures.
181pub(crate) fn signed_canonical_json(&self) -> Result<String, String> {
182 canonical_json(&self.signed).map_err(|error| {
183format!("invalid `third_party_invite.signed` field in `m.room.member` event: {error}")
184 })
185 }
186}