ruma_events/lib.rs
1#![doc(html_favicon_url = "https://ruma.dev/favicon.ico")]
2#![doc(html_logo_url = "https://ruma.dev/images/logo.png")]
3//! (De)serializable types for the events in the [Matrix](https://matrix.org) specification.
4//! These types are used by other Ruma crates.
5//!
6//! All data exchanged over Matrix is expressed as an event.
7//! Different event types represent different actions, such as joining a room or sending a message.
8//! Events are stored and transmitted as simple JSON structures.
9//! While anyone can create a new event type for their own purposes, the Matrix specification
10//! defines a number of event types which are considered core to the protocol.
11//! This module contains Rust types for all of the event types defined by the specification and
12//! facilities for extending the event system for custom event types.
13//!
14//! # Core event types
15//!
16//! This module includes Rust types for all event types in the Matrix specification.
17//! To better organize the crate, these types live in separate modules with a hierarchy that matches
18//! the reverse domain name notation of the event type. For example, the `m.room.message` event
19//! lives at `ruma::events::room::message::RoomMessageEvent`. Each type's module also contains a
20//! Rust type for that event type's `content` field, and any other supporting types required by the
21//! event's other fields.
22//!
23//! # Extending Ruma with custom events
24//!
25//! For our examples we will start with a simple custom state event. `ruma_event`
26//! specifies the state event's `type` and its `kind`.
27//!
28//! ```rust
29//! use ruma_events::macros::EventContent;
30//! use serde::{Deserialize, Serialize};
31//!
32//! #[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
33//! #[ruma_event(type = "org.example.event", kind = State, state_key_type = String)]
34//! pub struct ExampleContent {
35//! field: String,
36//! }
37//! ```
38//!
39//! This can be used with events structs, such as passing it into
40//! `ruma::api::client::state::send_state_event`'s `Request`.
41//!
42//! As a more advanced example we create a reaction message event. For this event we will use a
43//! [`OriginalSyncMessageLikeEvent`] struct but any [`OriginalMessageLikeEvent`] struct would work.
44//!
45//! ```rust
46//! use ruma_common::OwnedEventId;
47//! use ruma_events::{macros::EventContent, OriginalSyncMessageLikeEvent};
48//! use serde::{Deserialize, Serialize};
49//!
50//! #[derive(Clone, Debug, Deserialize, Serialize)]
51//! #[serde(tag = "rel_type")]
52//! pub enum RelatesTo {
53//! #[serde(rename = "m.annotation")]
54//! Annotation {
55//! /// The event this reaction relates to.
56//! event_id: OwnedEventId,
57//! /// The displayable content of the reaction.
58//! key: String,
59//! },
60//!
61//! /// Since this event is not fully specified in the Matrix spec
62//! /// it may change or types may be added, we are ready!
63//! #[serde(rename = "m.whatever")]
64//! Whatever,
65//! }
66//!
67//! /// The payload for our reaction event.
68//! #[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
69//! #[ruma_event(type = "m.reaction", kind = MessageLike)]
70//! pub struct ReactionEventContent {
71//! #[serde(rename = "m.relates_to")]
72//! pub relates_to: RelatesTo,
73//! }
74//!
75//! let json = serde_json::json!({
76//! "content": {
77//! "m.relates_to": {
78//! "event_id": "$xxxx-xxxx",
79//! "key": "👍",
80//! "rel_type": "m.annotation"
81//! }
82//! },
83//! "event_id": "$xxxx-xxxx",
84//! "origin_server_ts": 1,
85//! "sender": "@someone:example.org",
86//! "type": "m.reaction",
87//! "unsigned": {
88//! "age": 85
89//! }
90//! });
91//!
92//! // The downside of this event is we cannot use it with event enums,
93//! // but could be deserialized from a `Raw<_>` that has failed to deserialize.
94//! assert!(matches!(
95//! serde_json::from_value::<OriginalSyncMessageLikeEvent<ReactionEventContent>>(json),
96//! Ok(OriginalSyncMessageLikeEvent {
97//! content: ReactionEventContent {
98//! relates_to: RelatesTo::Annotation { key, .. },
99//! },
100//! ..
101//! }) if key == "👍"
102//! ));
103//! ```
104
105#![warn(missing_docs)]
106
107use std::{collections::BTreeSet, fmt};
108
109use ruma_common::{room_version_rules::RedactionRules, EventEncryptionAlgorithm, OwnedUserId};
110use serde::{de::IgnoredAny, Deserialize, Serialize, Serializer};
111
112// Needs to be public for trybuild tests
113#[doc(hidden)]
114pub mod _custom;
115mod content;
116mod enums;
117mod kinds;
118mod state_key;
119mod unsigned;
120
121// So event macros work inside this crate.
122extern crate self as ruma_events;
123
124/// Re-exports used by macro-generated code.
125///
126/// It is not considered part of this module's public API.
127#[doc(hidden)]
128pub mod exports {
129 pub use ruma_common;
130 pub use ruma_macros;
131 pub use serde;
132 pub use serde_json;
133}
134
135/// Re-export of all the derives needed to create your own event types.
136pub mod macros {
137 pub use ruma_macros::{Event, EventContent};
138}
139
140#[cfg(feature = "unstable-msc3927")]
141pub mod audio;
142#[cfg(feature = "unstable-msc3489")]
143pub mod beacon;
144#[cfg(feature = "unstable-msc3489")]
145pub mod beacon_info;
146pub mod call;
147pub mod direct;
148pub mod dummy;
149#[cfg(feature = "unstable-msc3954")]
150pub mod emote;
151#[cfg(feature = "unstable-msc3956")]
152pub mod encrypted;
153#[cfg(feature = "unstable-msc3551")]
154pub mod file;
155pub mod forwarded_room_key;
156pub mod fully_read;
157pub mod identity_server;
158pub mod ignored_user_list;
159#[cfg(feature = "unstable-msc3552")]
160pub mod image;
161#[cfg(feature = "unstable-msc2545")]
162pub mod image_pack;
163pub mod key;
164#[cfg(feature = "unstable-msc3488")]
165pub mod location;
166pub mod marked_unread;
167#[cfg(feature = "unstable-msc4278")]
168pub mod media_preview_config;
169#[cfg(feature = "unstable-msc4171")]
170pub mod member_hints;
171pub mod message;
172pub mod policy;
173#[cfg(feature = "unstable-msc3381")]
174pub mod poll;
175pub mod presence;
176pub mod push_rules;
177pub mod reaction;
178pub mod receipt;
179pub mod relation;
180pub mod room;
181pub mod room_key;
182#[cfg(feature = "unstable-msc4268")]
183pub mod room_key_bundle;
184pub mod room_key_request;
185#[cfg(feature = "unstable-msc4310")]
186pub mod rtc;
187pub mod secret;
188pub mod secret_storage;
189pub mod space;
190pub mod sticker;
191pub mod tag;
192pub mod typing;
193#[cfg(feature = "unstable-msc3553")]
194pub mod video;
195#[cfg(feature = "unstable-msc3245")]
196pub mod voice;
197
198pub use self::{
199 content::*,
200 enums::*,
201 kinds::*,
202 relation::{BundledMessageLikeRelations, BundledStateRelations},
203 state_key::EmptyStateKey,
204 unsigned::{MessageLikeUnsigned, RedactedUnsigned, StateUnsigned, UnsignedRoomRedactionEvent},
205};
206
207/// Trait to define the behavior of redact an event's content object.
208pub trait RedactContent {
209 /// The redacted form of the event's content.
210 type Redacted;
211
212 /// Transform `self` into a redacted form (removing most or all fields) according to the spec.
213 ///
214 /// A small number of events have room-version specific redaction behavior, so a
215 /// [`RedactionRules`] has to be specified.
216 fn redact(self, rules: &RedactionRules) -> Self::Redacted;
217}
218
219/// Helper struct to determine the event kind from a `serde_json::value::RawValue`.
220#[doc(hidden)]
221#[derive(Deserialize)]
222#[allow(clippy::exhaustive_structs)]
223pub struct EventTypeDeHelper<'a> {
224 #[serde(borrow, rename = "type")]
225 pub ev_type: std::borrow::Cow<'a, str>,
226}
227
228/// Helper struct to determine if an event has been redacted.
229#[doc(hidden)]
230#[derive(Deserialize)]
231#[allow(clippy::exhaustive_structs)]
232pub struct RedactionDeHelper {
233 /// Used to check whether redacted_because exists.
234 pub unsigned: Option<UnsignedDeHelper>,
235}
236
237#[doc(hidden)]
238#[derive(Deserialize)]
239#[allow(clippy::exhaustive_structs)]
240pub struct UnsignedDeHelper {
241 /// This is the field that signals an event has been redacted.
242 pub redacted_because: Option<IgnoredAny>,
243}
244
245/// Helper function for erroring when trying to serialize an event enum _Custom variant that can
246/// only be created by deserializing from an unknown event type.
247#[doc(hidden)]
248#[allow(clippy::ptr_arg)]
249pub fn serialize_custom_event_error<T, S: Serializer>(_: &T, _: S) -> Result<S::Ok, S::Error> {
250 Err(serde::ser::Error::custom(
251 "Failed to serialize event [content] enum: Unknown event type.\n\
252 To send custom events, turn them into `Raw<EnumType>` by going through
253 `serde_json::value::to_raw_value` and `Raw::from_json`.",
254 ))
255}
256
257/// Describes whether the event mentions other users or the room.
258#[derive(Clone, Debug, Default, Serialize, Deserialize)]
259#[non_exhaustive]
260pub struct Mentions {
261 /// The list of mentioned users.
262 ///
263 /// Defaults to an empty `BTreeSet`.
264 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
265 pub user_ids: BTreeSet<OwnedUserId>,
266
267 /// Whether the whole room is mentioned.
268 ///
269 /// Defaults to `false`.
270 #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
271 pub room: bool,
272}
273
274impl Mentions {
275 /// Create a `Mentions` with the default values.
276 pub fn new() -> Self {
277 Self::default()
278 }
279
280 /// Create a `Mentions` for the given user IDs.
281 pub fn with_user_ids(user_ids: impl IntoIterator<Item = OwnedUserId>) -> Self {
282 Self { user_ids: BTreeSet::from_iter(user_ids), ..Default::default() }
283 }
284
285 /// Create a `Mentions` for a room mention.
286 pub fn with_room_mention() -> Self {
287 Self { room: true, ..Default::default() }
288 }
289
290 fn add(&mut self, mentions: Self) {
291 self.user_ids.extend(mentions.user_ids);
292 self.room |= mentions.room;
293 }
294}
295
296// Wrapper around `Box<str>` that cannot be used in a meaningful way outside of
297// this crate. Used for string enums because their `_Custom` variant can't be
298// truly private (only `#[doc(hidden)]`).
299#[doc(hidden)]
300#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
301pub struct PrivOwnedStr(Box<str>);
302
303impl fmt::Debug for PrivOwnedStr {
304 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305 self.0.fmt(f)
306 }
307}