use std::ops::Deref;
use ruma_common::{CanonicalJsonObject, OwnedUserId, serde::from_raw_json_value};
use ruma_events::room::member::MembershipState;
use ruma_signatures::to_canonical_json_string_for_signing;
use serde::Deserialize;
use serde_json::value::RawValue as RawJsonValue;
use super::Event;
#[derive(Debug, Clone)]
pub struct RoomMemberEvent<E: Event>(E);
impl<E: Event> RoomMemberEvent<E> {
pub fn new(event: E) -> Self {
Self(event)
}
pub fn membership(&self) -> Result<MembershipState, String> {
RoomMemberEventContent(self.content()).membership()
}
pub fn join_authorised_via_users_server(&self) -> Result<Option<OwnedUserId>, String> {
RoomMemberEventContent(self.content()).join_authorised_via_users_server()
}
pub(crate) fn third_party_invite(&self) -> Result<Option<ThirdPartyInvite>, String> {
RoomMemberEventContent(self.content()).third_party_invite()
}
}
impl<E: Event> Deref for RoomMemberEvent<E> {
type Target = E;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub(crate) trait RoomMemberEventOptionExt {
fn membership(&self) -> Result<MembershipState, String>;
}
impl<E: Event> RoomMemberEventOptionExt for Option<RoomMemberEvent<E>> {
fn membership(&self) -> Result<MembershipState, String> {
match self {
Some(event) => event.membership(),
None => Ok(MembershipState::Leave),
}
}
}
pub(crate) struct RoomMemberEventContent<'a>(&'a RawJsonValue);
impl<'a> RoomMemberEventContent<'a> {
pub(crate) fn new(content: &'a RawJsonValue) -> Self {
Self(content)
}
}
impl RoomMemberEventContent<'_> {
pub(crate) fn membership(&self) -> Result<MembershipState, String> {
#[derive(Deserialize)]
struct RoomMemberContentMembership {
membership: MembershipState,
}
let content: RoomMemberContentMembership =
from_raw_json_value(self.0).map_err(|err: serde_json::Error| {
format!("missing or invalid `membership` field in `m.room.member` event: {err}")
})?;
Ok(content.membership)
}
pub(crate) fn join_authorised_via_users_server(&self) -> Result<Option<OwnedUserId>, String> {
#[derive(Deserialize)]
struct RoomMemberContentJoinAuthorizedViaUsersServer {
join_authorised_via_users_server: Option<OwnedUserId>,
}
let content: RoomMemberContentJoinAuthorizedViaUsersServer = from_raw_json_value(self.0)
.map_err(|err: serde_json::Error| {
format!(
"invalid `join_authorised_via_users_server` field in `m.room.member` event: {err}"
)
})?;
Ok(content.join_authorised_via_users_server)
}
pub(crate) fn third_party_invite(&self) -> Result<Option<ThirdPartyInvite>, String> {
#[derive(Deserialize)]
struct RoomMemberContentThirdPartyInvite {
third_party_invite: Option<ThirdPartyInvite>,
}
let content: RoomMemberContentThirdPartyInvite =
from_raw_json_value(self.0).map_err(|err: serde_json::Error| {
format!("invalid `third_party_invite` field in `m.room.member` event: {err}")
})?;
Ok(content.third_party_invite)
}
}
#[derive(Deserialize)]
pub(crate) struct ThirdPartyInvite {
signed: CanonicalJsonObject,
}
impl ThirdPartyInvite {
pub(crate) fn token(&self) -> Result<&str, String> {
let Some(token_value) = self.signed.get("token") else {
return Err("missing `token` field in `third_party_invite.signed` \
of `m.room.member` event"
.into());
};
token_value.as_str().ok_or_else(|| {
format!(
"unexpected format of `token` field in `third_party_invite.signed` \
of `m.room.member` event: expected string, got {token_value:?}"
)
})
}
pub(crate) fn mxid(&self) -> Result<&str, String> {
let Some(mxid_value) = self.signed.get("mxid") else {
return Err("missing `mxid` field in `third_party_invite.signed` \
of `m.room.member` event"
.into());
};
mxid_value.as_str().ok_or_else(|| {
format!(
"unexpected format of `mxid` field in `third_party_invite.signed` \
of `m.room.member` event: expected string, got {mxid_value:?}"
)
})
}
pub(crate) fn signatures(&self) -> Result<&CanonicalJsonObject, String> {
let Some(signatures_value) = self.signed.get("signatures") else {
return Err("missing `signatures` field in `third_party_invite.signed` \
of `m.room.member` event"
.into());
};
signatures_value.as_object().ok_or_else(|| {
format!(
"unexpected format of `signatures` field in `third_party_invite.signed` \
of `m.room.member` event: expected object, got {signatures_value:?}"
)
})
}
pub(crate) fn signed_canonical_json(&self) -> Result<String, String> {
to_canonical_json_string_for_signing(&self.signed).map_err(|error| {
format!("invalid `third_party_invite.signed` field in `m.room.member` event: {error}")
})
}
}