1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
//! (De)serializable types for the events in the [Matrix](https://matrix.org) specification.
//! These types are used by other ruma crates.
//!
//! All data exchanged over Matrix is expressed as an event.
//! Different event types represent different actions, such as joining a room or sending a message.
//! Events are stored and transmitted as simple JSON structures.
//! While anyone can create a new event type for their own purposes, the Matrix specification
//! defines a number of event types which are considered core to the protocol, and Matrix clients
//! and servers must understand their semantics.
//! ruma-events contains Rust types for each of the event types defined by the specification and
//! facilities for extending the event system for custom event types.
//!
//! # Event types
//!
//! ruma-events includes a Rust enum called `EventType`, which provides a simple enumeration of
//! all the event types defined by the Matrix specification. Matrix event types are serialized to
//! JSON strings in [reverse domain name
//! notation](https://en.wikipedia.org/wiki/Reverse_domain_name_notation), although the core event
//! types all use the special "m" TLD, e.g. *m.room.message*.
//!
//! # Core event types
//!
//! ruma-events includes Rust types for every one of the event types in the Matrix specification.
//! To better organize the crate, these types live in separate modules with a hierarchy that
//! matches the reverse domain name notation of the event type.
//! For example, the *m.room.message* event lives at `ruma_events::room::message::MessageEvent`.
//! Each type's module also contains a Rust type for that event type's `content` field, and any
//! other supporting types required by the event's other fields.
//!
//! # Extending Ruma with custom events
//!
//! For our example we will create a reaction message event. This can be used with ruma-events
//! structs, for this event we will use a `SyncMessageEvent` struct but any `MessageEvent` struct
//! would work.
//!
//! ```rust
//! use ruma_events::{macros::EventContent, SyncMessageEvent};
//! use ruma_identifiers::EventId;
//! use serde::{Deserialize, Serialize};
//!
//! #[derive(Clone, Debug, Deserialize, Serialize)]
//! #[serde(tag = "rel_type")]
//! pub enum RelatesTo {
//! #[serde(rename = "m.annotation")]
//! Annotation {
//! /// The event this reaction relates to.
//! event_id: EventId,
//! /// The displayable content of the reaction.
//! key: String,
//! },
//!
//! /// Since this event is not fully specified in the Matrix spec
//! /// it may change or types may be added, we are ready!
//! #[serde(rename = "m.whatever")]
//! Whatever,
//! }
//!
//! /// The payload for our reaction event.
//! #[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
//! #[ruma_event(type = "m.reaction", kind = Message)]
//! pub struct ReactionEventContent {
//! #[serde(rename = "m.relates_to")]
//! pub relates_to: RelatesTo,
//! }
//!
//! let json = serde_json::json!({
//! "content": {
//! "m.relates_to": {
//! "event_id": "$xxxx-xxxx",
//! "key": "👍",
//! "rel_type": "m.annotation"
//! }
//! },
//! "event_id": "$xxxx-xxxx",
//! "origin_server_ts": 1,
//! "sender": "@someone:example.org",
//! "type": "m.reaction",
//! "unsigned": {
//! "age": 85
//! }
//! });
//!
//! // The downside of this event is we cannot use it with event enums,
//! // but could be deserialized from a `Raw<_>` that has failed to deserialize.
//! matches::assert_matches!(
//! serde_json::from_value::<SyncMessageEvent<ReactionEventContent>>(json),
//! Ok(SyncMessageEvent {
//! content: ReactionEventContent {
//! relates_to: RelatesTo::Annotation { key, .. },
//! },
//! ..
//! }) if key == "👍"
//! );
//! ```
//!
//! # Serialization and deserialization
//!
//! All concrete event types in ruma-events can be serialized via the `Serialize` trait from
//! [serde](https://serde.rs/) and can be deserialized from as `Raw<EventType>`. In order to
//! handle incoming data that may not conform to `ruma-events`' strict definitions of event
//! structures, deserialization will return `Raw::Err` on error. This error covers both
//! structurally invalid JSON data as well as structurally valid JSON that doesn't fulfill
//! additional constraints the matrix specification defines for some event types. The error exposes
//! the deserialized `serde_json::Value` so that developers can still work with the received
//! event data. This makes it possible to deserialize a collection of events without the entire
//! collection failing to deserialize due to a single invalid event. The "content" type for each
//! event also implements `Serialize` and either `TryFromRaw` (enabling usage as
//! `Raw<ContentType>` for dedicated content types) or `Deserialize` (when the content is a
//! type alias), allowing content to be converted to and from JSON independently of the surrounding
//! event structure, if needed.
#![recursion_limit = "1024"]
#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
use std::fmt::Debug;
use ruma_identifiers::{EventEncryptionAlgorithm, RoomVersionId};
use ruma_serde::Raw;
use serde::{
de::{self, IgnoredAny},
Deserialize, Serialize,
};
use serde_json::value::RawValue as RawJsonValue;
use self::room::redaction::SyncRedactionEvent;
mod enums;
mod event_kinds;
mod unsigned;
// Hack to allow both ruma-events itself and external crates (or tests) to use procedural macros
// that expect `ruma_events` to exist in the prelude.
extern crate self as ruma_events;
/// Re-exports to allow users to declare their own event types using the
/// macros used internally.
///
/// It is not considered part of ruma-events' public API.
#[doc(hidden)]
pub mod exports {
pub use ruma_common;
pub use ruma_identifiers;
pub use ruma_serde;
pub use serde;
pub use serde_json;
}
/// Re-export of all the derives needed to create your own event types.
pub mod macros {
pub use ruma_events_macros::{Event, EventContent};
}
pub mod call;
pub mod custom;
pub mod direct;
pub mod dummy;
pub mod forwarded_room_key;
pub mod fully_read;
pub mod ignored_user_list;
pub mod key;
pub mod pdu;
pub mod policy;
pub mod presence;
pub mod push_rules;
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
pub mod reaction;
pub mod receipt;
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
pub mod relation;
pub mod room;
pub mod room_key;
pub mod room_key_request;
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
pub mod secret;
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
pub mod space;
pub mod sticker;
pub mod tag;
pub mod typing;
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
pub use self::relation::Relations;
pub use self::{
enums::*,
event_kinds::*,
unsigned::{RedactedUnsigned, Unsigned},
};
#[doc(hidden)]
#[cfg(feature = "compat")]
pub use unsigned::{RedactedUnsignedWithPrevContent, UnsignedWithPrevContent};
/// The base trait that all event content types implement.
///
/// Implementing this trait allows content types to be serialized as well as deserialized.
pub trait EventContent: Sized + Serialize {
/// A matrix event identifier, like `m.room.message`.
fn event_type(&self) -> &str;
/// Constructs the given event content.
fn from_parts(event_type: &str, content: &RawJsonValue) -> serde_json::Result<Self>;
}
/// Trait to define the behavior of redacting an event.
pub trait Redact {
/// The redacted form of the event.
type Redacted;
/// Transforms `self` into a redacted form (removing most fields) according to the spec.
///
/// A small number of events have room-version specific redaction behavior, so a version has to
/// be specified.
fn redact(self, redaction: SyncRedactionEvent, version: &RoomVersionId) -> Self::Redacted;
}
/// Trait to define the behavior of redact an event's content object.
pub trait RedactContent {
/// The redacted form of the event's content.
type Redacted;
/// Transform `self` into a redacted form (removing most or all fields) according to the spec.
///
/// A small number of events have room-version specific redaction behavior, so a version has to
/// be specified.
///
/// Where applicable, it is preferred to use [`Redact::redact`] on the outer event.
fn redact(self, version: &RoomVersionId) -> Self::Redacted;
}
/// Extension trait for [`Raw<_>`][ruma_serde::Raw].
pub trait RawExt<T: EventContent> {
/// Try to deserialize the JSON as an event's content.
fn deserialize_content(&self, event_type: &str) -> serde_json::Result<T>;
}
impl<T: EventContent> RawExt<T> for Raw<T> {
fn deserialize_content(&self, event_type: &str) -> serde_json::Result<T> {
T::from_parts(event_type, self.json())
}
}
/// Marker trait for the content of an ephemeral room event.
pub trait EphemeralRoomEventContent: EventContent {}
/// Marker trait for the content of a global account data event.
pub trait GlobalAccountDataEventContent: EventContent {}
/// Marker trait for the content of a room account data event.
pub trait RoomAccountDataEventContent: EventContent {}
/// Marker trait for the content of a to device event.
pub trait ToDeviceEventContent: EventContent {}
/// Marker trait for the content of a message event.
pub trait MessageEventContent: EventContent {}
/// Marker trait for the content of a state event.
pub trait StateEventContent: EventContent {}
/// The base trait that all redacted event content types implement.
///
/// This trait's associated functions and methods should not be used to build
/// redacted events, prefer the `redact` method on `AnyStateEvent` and
/// `AnyMessageEvent` and their "sync" and "stripped" counterparts. The
/// `RedactedEventContent` trait is an implementation detail, ruma makes no
/// API guarantees.
pub trait RedactedEventContent: EventContent {
/// Constructs the redacted event content.
///
/// If called for anything but "empty" redacted content this will error.
#[doc(hidden)]
fn empty(_event_type: &str) -> serde_json::Result<Self> {
Err(serde::de::Error::custom("this event is not redacted"))
}
/// Determines if the redacted event content needs to serialize fields.
#[doc(hidden)]
fn has_serialize_fields(&self) -> bool;
/// Determines if the redacted event content needs to deserialize fields.
#[doc(hidden)]
fn has_deserialize_fields() -> HasDeserializeFields;
}
/// Marker trait for the content of a redacted message event.
pub trait RedactedMessageEventContent: RedactedEventContent {}
/// Marker trait for the content of a redacted state event.
pub trait RedactedStateEventContent: RedactedEventContent {}
/// Trait for abstracting over event content structs.
///
/// … but *not* enums which don't always have an event type and kind (e.g. message vs state) that's
/// fixed / known at compile time.
pub trait StaticEventContent: EventContent {
/// The event's "kind".
///
/// See the type's documentation.
const KIND: EventKind;
/// The event type.
const TYPE: &'static str;
}
/// The "kind" of an event.
///
/// This corresponds directly to the event content marker traits.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum EventKind {
/// Global account data event kind.
GlobalAccountData,
/// Room account data event kind.
RoomAccountData,
/// Ephemeral room event kind.
EphemeralRoomData,
/// Message event kind.
///
/// Since redacted / non-redacted message events are used in the same places bu have different
/// sets of fields, these two variations are treated as two closely-related event kinds.
Message {
/// Redacted variation?
redacted: bool,
},
/// State event kind.
///
/// Since redacted / non-redacted state events are used in the same places bu have different
/// sets of fields, these two variations are treated as two closely-related event kinds.
State {
/// Redacted variation?
redacted: bool,
},
/// To-device event kind.
ToDevice,
/// Presence event kind.
Presence,
}
/// `HasDeserializeFields` is used in the code generated by the `Event` derive
/// to aid in deserializing redacted events.
#[doc(hidden)]
#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum HasDeserializeFields {
/// Deserialize the event's content, failing if invalid.
True,
/// Return the redacted version of this event's content.
False,
/// `Optional` is used for `RedactedAliasesEventContent` since it has
/// an empty version and one with content left after redaction that
/// must be supported together.
Optional,
}
/// Helper struct to determine the event kind from a `serde_json::value::RawValue`.
#[doc(hidden)]
#[derive(Deserialize)]
#[allow(clippy::exhaustive_structs)]
pub struct EventTypeDeHelper<'a> {
#[serde(borrow, rename = "type")]
pub ev_type: std::borrow::Cow<'a, str>,
}
/// Helper struct to determine if an event has been redacted.
#[doc(hidden)]
#[derive(Deserialize)]
#[allow(clippy::exhaustive_structs)]
pub struct RedactionDeHelper {
/// Used to check whether redacted_because exists.
pub unsigned: Option<UnsignedDeHelper>,
}
#[doc(hidden)]
#[derive(Deserialize)]
#[allow(clippy::exhaustive_structs)]
pub struct UnsignedDeHelper {
/// This is the field that signals an event has been redacted.
pub redacted_because: Option<IgnoredAny>,
}
/// Helper function for `serde_json::value::RawValue` deserialization.
#[doc(hidden)]
pub fn from_raw_json_value<'a, T, E>(val: &'a RawJsonValue) -> Result<T, E>
where
T: Deserialize<'a>,
E: de::Error,
{
serde_json::from_str(val.get()).map_err(E::custom)
}