use ruma_common::MilliSecondsSinceUnixEpoch;
use ruma_events_macros::{event_enum, EventEnumFromEvent};
use ruma_identifiers::{EventId, RoomId, RoomVersionId, UserId};
use ruma_serde::from_raw_json_value;
use serde::{de, Deserialize};
use serde_json::value::RawValue as RawJsonValue;
use crate::{
key,
room::{encrypted, message, redaction::SyncRoomRedactionEvent},
Redact, UnsignedDeHelper,
};
event_enum! {
enum GlobalAccountData {
"m.direct",
"m.ignored_user_list",
"m.push_rules",
}
enum RoomAccountData {
"m.fully_read",
"m.tag",
}
enum EphemeralRoom {
"m.receipt",
"m.typing",
}
enum Message {
"m.call.answer",
"m.call.invite",
"m.call.hangup",
"m.call.candidates",
"m.key.verification.ready",
"m.key.verification.start",
"m.key.verification.cancel",
"m.key.verification.accept",
"m.key.verification.key",
"m.key.verification.mac",
"m.key.verification.done",
#[cfg(feature = "unstable-msc2677")]
"m.reaction",
"m.room.encrypted",
"m.room.message",
"m.room.message.feedback",
"m.room.redaction",
"m.sticker",
}
enum State {
"m.policy.rule.room",
"m.policy.rule.server",
"m.policy.rule.user",
"m.room.aliases",
"m.room.avatar",
"m.room.canonical_alias",
"m.room.create",
"m.room.encryption",
"m.room.guest_access",
"m.room.history_visibility",
"m.room.join_rules",
"m.room.member",
"m.room.name",
"m.room.pinned_events",
"m.room.power_levels",
"m.room.server_acl",
"m.room.third_party_invite",
"m.room.tombstone",
"m.room.topic",
"m.space.child",
"m.space.parent",
}
enum ToDevice {
"m.dummy",
"m.room_key",
"m.room_key_request",
"m.forwarded_room_key",
"m.key.verification.request",
"m.key.verification.ready",
"m.key.verification.start",
"m.key.verification.cancel",
"m.key.verification.accept",
"m.key.verification.key",
"m.key.verification.mac",
"m.key.verification.done",
"m.room.encrypted",
"m.secret.request",
"m.secret.send",
}
}
macro_rules! doc_concat {
( $( #[doc = $doc:expr] $( $thing:tt )* )* ) => ( $( #[doc = $doc] $( $thing )* )* );
}
macro_rules! room_ev_accessor {
($field:ident: $ty:ty) => {
doc_concat! {
#[doc = concat!("Returns this event's `", stringify!($field), "` field.")]
pub fn $field(&self) -> $ty {
match self {
Self::Message(ev) => ev.$field(),
Self::State(ev) => ev.$field(),
Self::RedactedMessage(ev) => ev.$field(),
Self::RedactedState(ev) => ev.$field(),
}
}
}
};
}
#[allow(clippy::large_enum_variant, clippy::exhaustive_enums)]
#[derive(Clone, Debug, EventEnumFromEvent)]
pub enum AnyRoomEvent {
Message(AnyMessageEvent),
State(AnyStateEvent),
RedactedMessage(AnyRedactedMessageEvent),
RedactedState(AnyRedactedStateEvent),
}
impl AnyRoomEvent {
room_ev_accessor!(origin_server_ts: &MilliSecondsSinceUnixEpoch);
room_ev_accessor!(room_id: &RoomId);
room_ev_accessor!(event_id: &EventId);
room_ev_accessor!(sender: &UserId);
}
#[allow(clippy::large_enum_variant, clippy::exhaustive_enums)]
#[derive(Clone, Debug, EventEnumFromEvent)]
pub enum AnySyncRoomEvent {
Message(AnySyncMessageEvent),
State(AnySyncStateEvent),
RedactedMessage(AnyRedactedSyncMessageEvent),
RedactedState(AnyRedactedSyncStateEvent),
}
impl AnySyncRoomEvent {
room_ev_accessor!(origin_server_ts: &MilliSecondsSinceUnixEpoch);
room_ev_accessor!(event_id: &EventId);
room_ev_accessor!(sender: &UserId);
pub fn into_full_event(self, room_id: Box<RoomId>) -> AnyRoomEvent {
match self {
Self::Message(ev) => AnyRoomEvent::Message(ev.into_full_event(room_id)),
Self::State(ev) => AnyRoomEvent::State(ev.into_full_event(room_id)),
Self::RedactedMessage(ev) => AnyRoomEvent::RedactedMessage(ev.into_full_event(room_id)),
Self::RedactedState(ev) => AnyRoomEvent::RedactedState(ev.into_full_event(room_id)),
}
}
}
#[derive(Deserialize)]
#[allow(clippy::exhaustive_structs)]
struct EventDeHelper {
pub state_key: Option<de::IgnoredAny>,
pub unsigned: Option<UnsignedDeHelper>,
}
impl<'de> Deserialize<'de> for AnyRoomEvent {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
let EventDeHelper { state_key, unsigned } = from_raw_json_value(&json)?;
if state_key.is_some() {
Ok(match unsigned {
Some(unsigned) if unsigned.redacted_because.is_some() => {
AnyRoomEvent::RedactedState(from_raw_json_value(&json)?)
}
_ => AnyRoomEvent::State(from_raw_json_value(&json)?),
})
} else {
Ok(match unsigned {
Some(unsigned) if unsigned.redacted_because.is_some() => {
AnyRoomEvent::RedactedMessage(from_raw_json_value(&json)?)
}
_ => AnyRoomEvent::Message(from_raw_json_value(&json)?),
})
}
}
}
impl<'de> Deserialize<'de> for AnySyncRoomEvent {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
let EventDeHelper { state_key, unsigned } = from_raw_json_value(&json)?;
if state_key.is_some() {
Ok(match unsigned {
Some(unsigned) if unsigned.redacted_because.is_some() => {
AnySyncRoomEvent::RedactedState(from_raw_json_value(&json)?)
}
_ => AnySyncRoomEvent::State(from_raw_json_value(&json)?),
})
} else {
Ok(match unsigned {
Some(unsigned) if unsigned.redacted_because.is_some() => {
AnySyncRoomEvent::RedactedMessage(from_raw_json_value(&json)?)
}
_ => AnySyncRoomEvent::Message(from_raw_json_value(&json)?),
})
}
}
}
#[allow(clippy::large_enum_variant, clippy::exhaustive_enums)]
#[derive(Clone, Debug, EventEnumFromEvent)]
pub enum AnyRedactedRoomEvent {
Message(AnyRedactedMessageEvent),
State(AnyRedactedStateEvent),
}
impl Redact for AnyRoomEvent {
type Redacted = AnyRedactedRoomEvent;
fn redact(self, redaction: SyncRoomRedactionEvent, version: &RoomVersionId) -> Self::Redacted {
match self {
Self::Message(ev) => Self::Redacted::Message(ev.redact(redaction, version)),
Self::State(ev) => Self::Redacted::State(ev.redact(redaction, version)),
Self::RedactedMessage(ev) => Self::Redacted::Message(ev),
Self::RedactedState(ev) => Self::Redacted::State(ev),
}
}
}
impl From<AnyRedactedRoomEvent> for AnyRoomEvent {
fn from(ev: AnyRedactedRoomEvent) -> Self {
match ev {
AnyRedactedRoomEvent::Message(ev) => Self::RedactedMessage(ev),
AnyRedactedRoomEvent::State(ev) => Self::RedactedState(ev),
}
}
}
#[allow(clippy::large_enum_variant, clippy::exhaustive_enums)]
#[derive(Clone, Debug, EventEnumFromEvent)]
pub enum AnyRedactedSyncRoomEvent {
Message(AnyRedactedSyncMessageEvent),
State(AnyRedactedSyncStateEvent),
}
impl Redact for AnySyncRoomEvent {
type Redacted = AnyRedactedSyncRoomEvent;
fn redact(self, redaction: SyncRoomRedactionEvent, version: &RoomVersionId) -> Self::Redacted {
match self {
Self::Message(ev) => Self::Redacted::Message(ev.redact(redaction, version)),
Self::State(ev) => Self::Redacted::State(ev.redact(redaction, version)),
Self::RedactedMessage(ev) => Self::Redacted::Message(ev),
Self::RedactedState(ev) => Self::Redacted::State(ev),
}
}
}
impl From<AnyRedactedSyncRoomEvent> for AnySyncRoomEvent {
fn from(ev: AnyRedactedSyncRoomEvent) -> Self {
match ev {
AnyRedactedSyncRoomEvent::Message(ev) => Self::RedactedMessage(ev),
AnyRedactedSyncRoomEvent::State(ev) => Self::RedactedState(ev),
}
}
}
impl AnyMessageEventContent {
pub fn relation(&self) -> Option<encrypted::Relation> {
use crate::key::verification::{
accept::KeyVerificationAcceptEventContent, cancel::KeyVerificationCancelEventContent,
done::KeyVerificationDoneEventContent, key::KeyVerificationKeyEventContent,
mac::KeyVerificationMacEventContent, ready::KeyVerificationReadyEventContent,
start::KeyVerificationStartEventContent,
};
match self {
#[rustfmt::skip]
Self::KeyVerificationReady(KeyVerificationReadyEventContent { relates_to, .. })
| Self::KeyVerificationStart(KeyVerificationStartEventContent { relates_to, .. })
| Self::KeyVerificationCancel(KeyVerificationCancelEventContent { relates_to, .. })
| Self::KeyVerificationAccept(KeyVerificationAcceptEventContent { relates_to, .. })
| Self::KeyVerificationKey(KeyVerificationKeyEventContent { relates_to, .. })
| Self::KeyVerificationMac(KeyVerificationMacEventContent { relates_to, .. })
| Self::KeyVerificationDone(KeyVerificationDoneEventContent { relates_to, .. }) => {
let key::verification::Relation { event_id } = relates_to;
Some(encrypted::Relation::Reference(encrypted::Reference {
event_id: event_id.clone(),
}))
}
#[cfg(feature = "unstable-msc2677")]
Self::Reaction(ev) => {
use crate::reaction;
let reaction::Relation { event_id, emoji } = &ev.relates_to;
Some(encrypted::Relation::Annotation(encrypted::Annotation {
event_id: event_id.clone(),
key: emoji.clone(),
}))
}
Self::RoomEncrypted(ev) => ev.relates_to.clone(),
Self::RoomMessage(ev) => ev.relates_to.clone().map(|rel| match rel {
message::Relation::Reply { in_reply_to } => {
encrypted::Relation::Reply { in_reply_to }
}
#[cfg(feature = "unstable-msc2676")]
message::Relation::Replacement(re) => {
encrypted::Relation::Replacement(encrypted::Replacement {
event_id: re.event_id,
})
}
message::Relation::_Custom => encrypted::Relation::_Custom,
}),
Self::CallAnswer(_)
| Self::CallInvite(_)
| Self::CallHangup(_)
| Self::CallCandidates(_)
| Self::RoomMessageFeedback(_)
| Self::RoomRedaction(_)
| Self::Sticker(_)
| Self::_Custom { .. } => None,
}
}
}