use ruma_events_macros::event_content_enum; use serde::{ de::{self, DeserializeOwned, Error as _}, Deserialize, Serialize, }; use serde_json::value::RawValue as RawJsonValue; use crate::{ event_kinds::{ BasicEvent, EphemeralRoomEvent, MessageEvent, MessageEventStub, StateEvent, StateEventStub, StrippedStateEventStub, ToDeviceEvent, }, presence::PresenceEvent, room::redaction::{RedactionEvent, RedactionEventStub}, }; event_content_enum! { /// Any basic event. name: AnyBasicEventContent, events: [ "m.direct", "m.dummy", "m.ignored_user_list", "m.push_rules", "m.room_key", "m.tag", ] } event_content_enum! { /// Any ephemeral room event. name: AnyEphemeralRoomEventContent, events: [ "m.fully_read", "m.receipt", "m.typing", ] } event_content_enum! { /// Any message event. name: AnyMessageEventContent, events: [ "m.call.answer", "m.call.invite", "m.call.hangup", "m.call.candidates", "m.room.encrypted", "m.room.message", "m.room.message.feedback", "m.sticker", ] } event_content_enum! { /// Any state event. name: AnyStateEventContent, events: [ "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.redaction", "m.room.server_acl", "m.room.third_party_invite", "m.room.tombstone", "m.room.topic", ] } event_content_enum! { /// Any to-device event. name: AnyToDeviceEventContent, events: [ "m.dummy", "m.room_key", "m.room_key_request", "m.forwarded_room_key", "m.key.verification.request", "m.key.verification.start", "m.key.verification.cancel", "m.key.verification.accept", "m.key.verification.key", "m.key.verification.mac", "m.room.encrypted", ] } /// Any basic event, one that has no (well-known) fields outside of `content`. pub type AnyBasicEvent = BasicEvent; /// Any ephemeral room event. pub type AnyEphemeralRoomEvent = EphemeralRoomEvent; /// Any message event. pub type AnyMessageEvent = MessageEvent; /// Any message event stub (message event without a `room_id`, as returned in `/sync` responses) pub type AnyMessageEventStub = MessageEventStub; /// Any state event. pub type AnyStateEvent = StateEvent; /// Any state event stub (state event without a `room_id`, as returned in `/sync` responses) pub type AnyStateEventStub = StateEventStub; /// Any stripped state event stub (stripped-down state event, as returned for rooms the user has /// been invited to in `/sync` responses) pub type AnyStrippedStateEventStub = StrippedStateEventStub; /// Any to-device event. pub type AnyToDeviceEvent = ToDeviceEvent; /// Any event. #[derive(Clone, Debug, Serialize)] #[serde(untagged)] pub enum AnyEvent { /// Any basic event. Basic(AnyBasicEvent), /// `"m.presence"`, the only non-room event with a `sender` field. Presence(PresenceEvent), /// Any ephemeral room event. Ephemeral(AnyEphemeralRoomEvent), /// Any message event. Message(AnyMessageEvent), /// `"m.room.redaction"`, the only room event with a `redacts` field. Redaction(RedactionEvent), /// Any state event. State(AnyStateEvent), } /// Any room event. #[derive(Clone, Debug, Serialize)] #[serde(untagged)] pub enum AnyRoomEvent { /// Any message event. Message(AnyMessageEvent), /// `"m.room.redaction"`, the only room event with a `redacts` field. Redaction(RedactionEvent), /// Any state event. State(AnyStateEvent), } /// Any room event stub (room event without a `room_id`, as returned in `/sync` responses) #[derive(Clone, Debug, Serialize)] #[serde(untagged)] pub enum AnyRoomEventStub { /// Any message event stub Message(AnyMessageEventStub), /// `"m.room.redaction"` stub Redaction(RedactionEventStub), /// Any state event stub State(AnyStateEventStub), } #[derive(Deserialize)] struct EventDeHelper { #[serde(rename = "type")] ev_type: String, } fn from_raw_json_value(val: &RawJsonValue) -> Result where T: DeserializeOwned, E: de::Error, { serde_json::from_str(val.get()).map_err(E::custom) } // FIXME `#[serde(untagged)]` deserialization fails for these enums which // is odd as we are doing basically the same thing here, investigate? impl<'de> de::Deserialize<'de> for AnyEvent { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { let json = Box::::deserialize(deserializer)?; let EventDeHelper { ev_type } = from_raw_json_value(&json)?; match ev_type.as_str() { "m.room.redaction" => Ok(AnyEvent::Redaction(from_raw_json_value(&json)?)), "m.presence" => Ok(AnyEvent::Presence(from_raw_json_value(&json)?)), ev_type if AnyBasicEventContent::is_compatible(ev_type) => { Ok(AnyEvent::Basic(from_raw_json_value(&json)?)) } ev_type if AnyEphemeralRoomEventContent::is_compatible(ev_type) => { Ok(AnyEvent::Ephemeral(from_raw_json_value(&json)?)) } ev_type if AnyMessageEventContent::is_compatible(ev_type) => { Ok(AnyEvent::Message(from_raw_json_value(&json)?)) } ev_type if AnyStateEventContent::is_compatible(ev_type) => { Ok(AnyEvent::State(from_raw_json_value(&json)?)) } _ => Err(D::Error::custom(format!("event type `{}` is not a valid event", ev_type))), } } } impl<'de> de::Deserialize<'de> for AnyRoomEvent { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { let json = Box::::deserialize(deserializer)?; let EventDeHelper { ev_type } = from_raw_json_value(&json)?; match ev_type.as_str() { "m.room.redaction" => Ok(AnyRoomEvent::Redaction(from_raw_json_value(&json)?)), ev_type if AnyMessageEventContent::is_compatible(ev_type) => { Ok(AnyRoomEvent::Message(from_raw_json_value(&json)?)) } ev_type if AnyStateEventContent::is_compatible(ev_type) => { Ok(AnyRoomEvent::State(from_raw_json_value(&json)?)) } _ => Err(D::Error::custom(format!("event type `{}` is not a valid event", ev_type))), } } } impl<'de> de::Deserialize<'de> for AnyRoomEventStub { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { let json = Box::::deserialize(deserializer)?; let EventDeHelper { ev_type } = from_raw_json_value(&json)?; match ev_type.as_str() { "m.room.redaction" => Ok(AnyRoomEventStub::Redaction(from_raw_json_value(&json)?)), ev_type if AnyMessageEventContent::is_compatible(ev_type) => { Ok(AnyRoomEventStub::Message(from_raw_json_value(&json)?)) } ev_type if AnyStateEventContent::is_compatible(ev_type) => { Ok(AnyRoomEventStub::State(from_raw_json_value(&json)?)) } _ => Err(D::Error::custom(format!("event type `{}` is not a valid event", ev_type))), } } }