use ruma_macros::{Event, EventContent};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::value::RawValue as RawJsonValue;
use crate::{
events::{
EventContent, MessageLikeEventType, MessageLikeUnsigned, Redact, RedactContent,
RedactedUnsigned, RedactionDeHelper,
},
serde::from_raw_json_value,
EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId,
};
#[allow(clippy::exhaustive_enums)]
#[derive(Clone, Debug, Serialize)]
#[serde(untagged)]
pub enum RoomRedactionEvent {
Original(OriginalRoomRedactionEvent),
Redacted(RedactedRoomRedactionEvent),
}
#[allow(clippy::exhaustive_enums)]
#[derive(Clone, Debug, Serialize)]
#[serde(untagged)]
pub enum SyncRoomRedactionEvent {
Original(OriginalSyncRoomRedactionEvent),
Redacted(RedactedSyncRoomRedactionEvent),
}
#[derive(Clone, Debug, Event)]
#[allow(clippy::exhaustive_structs)]
pub struct OriginalRoomRedactionEvent {
pub content: RoomRedactionEventContent,
pub redacts: OwnedEventId,
pub event_id: OwnedEventId,
pub sender: OwnedUserId,
pub origin_server_ts: MilliSecondsSinceUnixEpoch,
pub room_id: OwnedRoomId,
pub unsigned: MessageLikeUnsigned,
}
impl Redact for OriginalRoomRedactionEvent {
type Redacted = RedactedRoomRedactionEvent;
fn redact(
self,
redaction: SyncRoomRedactionEvent,
version: &crate::RoomVersionId,
) -> Self::Redacted {
RedactedRoomRedactionEvent {
content: self.content.redact(version),
event_id: self.event_id,
sender: self.sender,
origin_server_ts: self.origin_server_ts,
room_id: self.room_id,
unsigned: RedactedUnsigned::new_because(Box::new(redaction)),
}
}
}
#[derive(Clone, Debug, Event)]
#[allow(clippy::exhaustive_structs)]
pub struct RedactedRoomRedactionEvent {
pub content: RedactedRoomRedactionEventContent,
pub event_id: OwnedEventId,
pub sender: OwnedUserId,
pub origin_server_ts: MilliSecondsSinceUnixEpoch,
pub room_id: OwnedRoomId,
pub unsigned: RedactedUnsigned,
}
#[derive(Clone, Debug, Event)]
#[allow(clippy::exhaustive_structs)]
pub struct OriginalSyncRoomRedactionEvent {
pub content: RoomRedactionEventContent,
pub redacts: OwnedEventId,
pub event_id: OwnedEventId,
pub sender: OwnedUserId,
pub origin_server_ts: MilliSecondsSinceUnixEpoch,
pub unsigned: MessageLikeUnsigned,
}
impl Redact for OriginalSyncRoomRedactionEvent {
type Redacted = RedactedSyncRoomRedactionEvent;
fn redact(
self,
redaction: SyncRoomRedactionEvent,
version: &crate::RoomVersionId,
) -> Self::Redacted {
RedactedSyncRoomRedactionEvent {
content: self.content.redact(version),
event_id: self.event_id,
sender: self.sender,
origin_server_ts: self.origin_server_ts,
unsigned: RedactedUnsigned::new_because(Box::new(redaction)),
}
}
}
#[derive(Clone, Debug, Event)]
#[allow(clippy::exhaustive_structs)]
pub struct RedactedSyncRoomRedactionEvent {
pub content: RedactedRoomRedactionEventContent,
pub event_id: OwnedEventId,
pub sender: OwnedUserId,
pub origin_server_ts: MilliSecondsSinceUnixEpoch,
pub unsigned: RedactedUnsigned,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "m.room.redaction", kind = MessageLike)]
pub struct RoomRedactionEventContent {
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
impl RoomRedactionEventContent {
pub fn new() -> Self {
Self::default()
}
pub fn with_reason(reason: String) -> Self {
Self { reason: Some(reason) }
}
}
impl RoomRedactionEvent {
pub fn event_type(&self) -> MessageLikeEventType {
match self {
Self::Original(ev) => ev.content.event_type(),
Self::Redacted(ev) => ev.content.event_type(),
}
}
pub fn event_id(&self) -> &EventId {
match self {
Self::Original(ev) => &ev.event_id,
Self::Redacted(ev) => &ev.event_id,
}
}
pub fn sender(&self) -> &UserId {
match self {
Self::Original(ev) => &ev.sender,
Self::Redacted(ev) => &ev.sender,
}
}
pub fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch {
match self {
Self::Original(ev) => ev.origin_server_ts,
Self::Redacted(ev) => ev.origin_server_ts,
}
}
pub fn room_id(&self) -> &RoomId {
match self {
Self::Original(ev) => &ev.room_id,
Self::Redacted(ev) => &ev.room_id,
}
}
pub fn as_original(&self) -> Option<&OriginalRoomRedactionEvent> {
match self {
Self::Original(v) => Some(v),
_ => None,
}
}
}
impl Redact for RoomRedactionEvent {
type Redacted = Self;
fn redact(self, redaction: SyncRoomRedactionEvent, version: &crate::RoomVersionId) -> Self {
match self {
Self::Original(ev) => Self::Redacted(ev.redact(redaction, version)),
Self::Redacted(ev) => Self::Redacted(ev),
}
}
}
impl<'de> Deserialize<'de> for RoomRedactionEvent {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
let RedactionDeHelper { unsigned } = from_raw_json_value(&json)?;
if unsigned.and_then(|u| u.redacted_because).is_some() {
Ok(Self::Redacted(from_raw_json_value(&json)?))
} else {
Ok(Self::Original(from_raw_json_value(&json)?))
}
}
}
impl SyncRoomRedactionEvent {
pub fn event_type(&self) -> MessageLikeEventType {
match self {
Self::Original(ev) => ev.content.event_type(),
Self::Redacted(ev) => ev.content.event_type(),
}
}
pub fn event_id(&self) -> &EventId {
match self {
Self::Original(ev) => &ev.event_id,
Self::Redacted(ev) => &ev.event_id,
}
}
pub fn sender(&self) -> &UserId {
match self {
Self::Original(ev) => &ev.sender,
Self::Redacted(ev) => &ev.sender,
}
}
pub fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch {
match self {
Self::Original(ev) => ev.origin_server_ts,
Self::Redacted(ev) => ev.origin_server_ts,
}
}
pub fn as_original(&self) -> Option<&OriginalSyncRoomRedactionEvent> {
match self {
Self::Original(v) => Some(v),
_ => None,
}
}
pub fn into_full_event(self, room_id: OwnedRoomId) -> RoomRedactionEvent {
match self {
Self::Original(ev) => RoomRedactionEvent::Original(ev.into_full_event(room_id)),
Self::Redacted(ev) => RoomRedactionEvent::Redacted(ev.into_full_event(room_id)),
}
}
}
impl From<RoomRedactionEvent> for SyncRoomRedactionEvent {
fn from(full: RoomRedactionEvent) -> Self {
match full {
RoomRedactionEvent::Original(ev) => Self::Original(ev.into()),
RoomRedactionEvent::Redacted(ev) => Self::Redacted(ev.into()),
}
}
}
impl Redact for SyncRoomRedactionEvent {
type Redacted = Self;
fn redact(self, redaction: SyncRoomRedactionEvent, version: &crate::RoomVersionId) -> Self {
match self {
Self::Original(ev) => Self::Redacted(ev.redact(redaction, version)),
Self::Redacted(ev) => Self::Redacted(ev),
}
}
}
impl<'de> Deserialize<'de> for SyncRoomRedactionEvent {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
let RedactionDeHelper { unsigned } = from_raw_json_value(&json)?;
if unsigned.and_then(|u| u.redacted_because).is_some() {
Ok(Self::Redacted(from_raw_json_value(&json)?))
} else {
Ok(Self::Original(from_raw_json_value(&json)?))
}
}
}