Skip to main content

arminius_lib_events/
pdu.rs

1// SPDX-FileCopyrightText: 2025 BWI GmbH
2//
3// SPDX-License-Identifier: Apache-2.0
4
5//! PDU (Persistent Data Unit) representations for different room version
6//! formats.
7//!
8//! This module provides the PDU types used in Matrix federation:
9//!
10//! - [`V1Pdu`]: Room versions 1-2 with hashed event references
11//! - [`V3Pdu`]: Room versions 3-5 with plain event references
12//! - [`V6Pdu`]: Room versions 6-11 with plain event references
13//! - [`V12Pdu`]: Room version 12+ format
14//! - [`Pdu`]: Enum wrapper for version-agnostic handling
15
16use ruma::{
17	EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId,
18	events::TimelineEventType, state_res::Event as StateResEvent,
19};
20use serde::{Serialize, Serializer};
21use serde_json::value::RawValue as RawJsonValue;
22
23use crate::types::HashedEventRef;
24
25/// Client event representation.
26pub type ClientEvent = Box<RawJsonValue>;
27
28/// Helper trait for extracting event IDs from different reference formats.
29trait AsEventId {
30	/// Returns a reference to the event ID.
31	fn as_event_id(&self) -> &OwnedEventId;
32}
33
34impl AsEventId for OwnedEventId {
35	fn as_event_id(&self) -> &OwnedEventId {
36		self
37	}
38}
39
40impl AsEventId for HashedEventRef {
41	fn as_event_id(&self) -> &OwnedEventId {
42		&self.event_id
43	}
44}
45
46/// Common PDU fields independent of room version format.
47#[derive(Debug, Clone)]
48pub struct PduCommon {
49	/// Event ID.
50	pub(crate) event_id: OwnedEventId,
51	/// Room ID.
52	pub(crate) room_id: OwnedRoomId,
53	/// Event sender.
54	pub(crate) sender: OwnedUserId,
55	/// Event type.
56	pub(crate) event_type: TimelineEventType,
57	/// Event content.
58	pub(crate) content: Box<RawJsonValue>,
59	/// State key for state events.
60	pub(crate) state_key: Option<String>,
61	/// Origin server timestamp.
62	pub(crate) origin_server_ts: MilliSecondsSinceUnixEpoch,
63	/// Redacted event ID.
64	pub(crate) redacts: Option<OwnedEventId>,
65	/// Whether the event is rejected.
66	pub(crate) rejected: bool,
67	/// Raw PDU JSON.
68	pub(crate) raw: Box<RawJsonValue>,
69}
70
71impl PduCommon {
72	/// Get the event ID
73	#[must_use]
74	pub fn event_id(&self) -> &EventId {
75		&self.event_id
76	}
77
78	/// Get the room ID
79	#[must_use]
80	pub fn room_id(&self) -> &RoomId {
81		&self.room_id
82	}
83
84	/// Get the sender
85	#[must_use]
86	pub fn sender(&self) -> &UserId {
87		&self.sender
88	}
89
90	/// Get the event type
91	#[must_use]
92	pub const fn event_type(&self) -> &TimelineEventType {
93		&self.event_type
94	}
95
96	/// Get the event content
97	#[must_use]
98	pub fn content(&self) -> &RawJsonValue {
99		&self.content
100	}
101
102	/// Get the state key
103	#[must_use]
104	pub fn state_key(&self) -> Option<&str> {
105		self.state_key.as_deref()
106	}
107
108	/// Get the origin server timestamp
109	#[must_use]
110	pub const fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch {
111		self.origin_server_ts
112	}
113
114	/// Get the redacted event ID
115	#[must_use]
116	pub fn redacts(&self) -> Option<&EventId> {
117		self.redacts.as_deref()
118	}
119
120	/// Check if the event is rejected
121	#[must_use]
122	pub const fn rejected(&self) -> bool {
123		self.rejected
124	}
125
126	/// Get the raw PDU JSON
127	#[must_use]
128	pub fn raw(&self) -> &RawJsonValue {
129		&self.raw
130	}
131}
132
133/// PDU representation for room version formats V1/V2.
134#[derive(Debug, Clone)]
135pub struct V1Pdu {
136	/// Common PDU fields.
137	pub common: PduCommon,
138	/// Previous event references with hashes.
139	pub prev_events: Vec<OwnedEventId>,
140	/// Auth event references with hashes.
141	pub auth_events: Vec<OwnedEventId>,
142	/// Depth of the event
143	pub depth: u64,
144}
145
146/// PDU representation for room version formats V3-V5.
147#[derive(Debug, Clone)]
148pub struct V3Pdu {
149	/// Common PDU fields.
150	pub common: PduCommon,
151	/// Previous event IDs.
152	pub prev_events: Vec<OwnedEventId>,
153	/// Auth event IDs.
154	pub auth_events: Vec<OwnedEventId>,
155	/// Depth of the event
156	pub depth: u64,
157}
158
159/// PDU representation for room version formats V6-V11.
160#[derive(Debug, Clone)]
161pub struct V6Pdu {
162	/// Common PDU fields.
163	pub common: PduCommon,
164	/// Previous event IDs.
165	pub prev_events: Vec<OwnedEventId>,
166	/// Auth event IDs.
167	pub auth_events: Vec<OwnedEventId>,
168	/// Depth of the event
169	pub depth: u64,
170}
171
172/// PDU representation for room version format V12.
173#[derive(Debug, Clone)]
174pub struct V12Pdu {
175	/// Common PDU fields.
176	pub common: PduCommon,
177	/// Previous event IDs.
178	pub prev_events: Vec<OwnedEventId>,
179	/// Auth event IDs.
180	pub auth_events: Vec<OwnedEventId>,
181	/// Depth of the event
182	pub depth: u64,
183}
184
185/// Supported PDU representations for room versions.
186#[derive(Debug, Clone)]
187pub enum Pdu {
188	/// PDU for room versions using the V1 event format.
189	V1(V1Pdu),
190	/// PDU for room versions V3-V5.
191	V3(V3Pdu),
192	/// PDU for room versions V6-V11.
193	V6(V6Pdu),
194	/// PDU for room version V12.
195	V12(V12Pdu),
196}
197
198/// Implements the [`StateResEvent`] trait for PDU types.
199macro_rules! impl_state_res_event {
200	($pdu_type:ty) => {
201		impl StateResEvent for $pdu_type {
202			type Id = OwnedEventId;
203			fn event_id(&self) -> &Self::Id {
204				&self.common.event_id
205			}
206			fn room_id(&self) -> Option<&ruma::RoomId> {
207				Some(&self.common.room_id)
208			}
209			fn sender(&self) -> &ruma::UserId {
210				&self.common.sender
211			}
212			fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch {
213				self.common.origin_server_ts
214			}
215			fn event_type(&self) -> &ruma::events::TimelineEventType {
216				&self.common.event_type
217			}
218			fn content(&self) -> &RawJsonValue {
219				self.common.content.as_ref()
220			}
221			fn state_key(&self) -> Option<&str> {
222				self.common.state_key.as_deref()
223			}
224			fn prev_events(&self) -> Box<dyn DoubleEndedIterator<Item = &Self::Id> + '_> {
225				Box::new(self.prev_events.iter().map(AsEventId::as_event_id))
226			}
227			fn auth_events(&self) -> Box<dyn DoubleEndedIterator<Item = &Self::Id> + '_> {
228				Box::new(self.auth_events.iter().map(AsEventId::as_event_id))
229			}
230			fn redacts(&self) -> Option<&Self::Id> {
231				self.common.redacts.as_ref()
232			}
233			fn rejected(&self) -> bool {
234				self.common.rejected
235			}
236		}
237	};
238}
239
240impl_state_res_event!(V1Pdu);
241impl_state_res_event!(V3Pdu);
242impl_state_res_event!(V6Pdu);
243impl_state_res_event!(V12Pdu);
244
245impl StateResEvent for Pdu {
246	type Id = OwnedEventId;
247
248	fn event_id(&self) -> &Self::Id {
249		match self {
250			Self::V1(pdu) => pdu.event_id(),
251			Self::V3(pdu) => pdu.event_id(),
252			Self::V6(pdu) => pdu.event_id(),
253			Self::V12(pdu) => pdu.event_id(),
254		}
255	}
256
257	fn room_id(&self) -> Option<&RoomId> {
258		match self {
259			Self::V1(pdu) => pdu.room_id(),
260			Self::V3(pdu) => pdu.room_id(),
261			Self::V6(pdu) => pdu.room_id(),
262			Self::V12(pdu) => pdu.room_id(),
263		}
264	}
265
266	fn sender(&self) -> &UserId {
267		match self {
268			Self::V1(pdu) => pdu.sender(),
269			Self::V3(pdu) => pdu.sender(),
270			Self::V6(pdu) => pdu.sender(),
271			Self::V12(pdu) => pdu.sender(),
272		}
273	}
274
275	fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch {
276		match self {
277			Self::V1(pdu) => pdu.origin_server_ts(),
278			Self::V3(pdu) => pdu.origin_server_ts(),
279			Self::V6(pdu) => pdu.origin_server_ts(),
280			Self::V12(pdu) => pdu.origin_server_ts(),
281		}
282	}
283
284	fn event_type(&self) -> &TimelineEventType {
285		match self {
286			Self::V1(pdu) => pdu.event_type(),
287			Self::V3(pdu) => pdu.event_type(),
288			Self::V6(pdu) => pdu.event_type(),
289			Self::V12(pdu) => pdu.event_type(),
290		}
291	}
292
293	fn content(&self) -> &RawJsonValue {
294		match self {
295			Self::V1(pdu) => pdu.content(),
296			Self::V3(pdu) => pdu.content(),
297			Self::V6(pdu) => pdu.content(),
298			Self::V12(pdu) => pdu.content(),
299		}
300	}
301
302	fn state_key(&self) -> Option<&str> {
303		match self {
304			Self::V1(pdu) => pdu.state_key(),
305			Self::V3(pdu) => pdu.state_key(),
306			Self::V6(pdu) => pdu.state_key(),
307			Self::V12(pdu) => pdu.state_key(),
308		}
309	}
310
311	fn prev_events(&self) -> Box<dyn DoubleEndedIterator<Item = &Self::Id> + '_> {
312		match self {
313			Self::V1(pdu) => pdu.prev_events(),
314			Self::V3(pdu) => pdu.prev_events(),
315			Self::V6(pdu) => pdu.prev_events(),
316			Self::V12(pdu) => pdu.prev_events(),
317		}
318	}
319
320	fn auth_events(&self) -> Box<dyn DoubleEndedIterator<Item = &Self::Id> + '_> {
321		match self {
322			Self::V1(pdu) => pdu.auth_events(),
323			Self::V3(pdu) => pdu.auth_events(),
324			Self::V6(pdu) => pdu.auth_events(),
325			Self::V12(pdu) => pdu.auth_events(),
326		}
327	}
328
329	fn redacts(&self) -> Option<&Self::Id> {
330		match self {
331			Self::V1(pdu) => pdu.redacts(),
332			Self::V3(pdu) => pdu.redacts(),
333			Self::V6(pdu) => pdu.redacts(),
334			Self::V12(pdu) => pdu.redacts(),
335		}
336	}
337
338	fn rejected(&self) -> bool {
339		match self {
340			Self::V1(pdu) => pdu.rejected(),
341			Self::V3(pdu) => pdu.rejected(),
342			Self::V6(pdu) => pdu.rejected(),
343			Self::V12(pdu) => pdu.rejected(),
344		}
345	}
346}
347
348/// A PDU serializes as the federation JSON returned by [`Pdu::raw`].
349impl Serialize for Pdu {
350	fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
351		self.raw().serialize(serializer)
352	}
353}
354
355impl Pdu {
356	/// Get the raw PDU which can be sent to other homeservers
357	#[must_use]
358	pub fn raw(&self) -> &serde_json::value::RawValue {
359		match self {
360			Pdu::V1(pdu) => &pdu.common.raw,
361			Pdu::V3(pdu) => &pdu.common.raw,
362			Pdu::V6(pdu) => &pdu.common.raw,
363			Pdu::V12(pdu) => &pdu.common.raw,
364		}
365	}
366
367	/// Get the `depth`
368	#[must_use]
369	pub fn depth(&self) -> u64 {
370		match self {
371			Pdu::V1(pdu) => pdu.depth,
372			Pdu::V3(pdu) => pdu.depth,
373			Pdu::V6(pdu) => pdu.depth,
374			Pdu::V12(pdu) => pdu.depth,
375		}
376	}
377
378	/// Get the `event_id`
379	#[expect(
380		clippy::same_name_method,
381		reason = " Duplicates `state_res::Pdu`. But we don't want other crates to include `state_res` "
382	)]
383	#[must_use]
384	pub fn event_id(&self) -> &EventId {
385		match self {
386			Pdu::V1(pdu) => pdu.event_id(),
387			Pdu::V3(pdu) => pdu.event_id(),
388			Pdu::V6(pdu) => pdu.event_id(),
389			Pdu::V12(pdu) => pdu.event_id(),
390		}
391	}
392}
393
394mod tests;