1#![allow(
12 clippy::allow_attributes,
13 reason = "This source file contains enums which allow `clippy::missing_docs_in_private_items`. But an exception to this lint will mistakenly report that it's unfulfilled when used with expect, even though it is not."
14)]
15
16use std::{iter, str::FromStr};
17
18use ruma::{
19 EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, canonical_json,
20 room_version_rules::{EventsReferenceFormatVersion, RoomVersionRules},
21};
22use serde::{Deserialize, Deserializer, Serialize};
23
24use crate::{
25 conversions::serialize_hashed_event_ref,
26 errors::{UnknownRoomVersionError, UnknownRoomVersionStringError},
27};
28
29pub type StateKey = String;
31
32#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
37pub struct EventHash {
38 sha256: String,
40}
41
42impl EventHash {
43 #[must_use]
45 pub fn new(sha256: impl Into<String>) -> Self {
46 Self { sha256: sha256.into() }
47 }
48
49 #[must_use]
51 pub fn sha256(&self) -> &str {
52 &self.sha256
53 }
54
55 #[must_use]
60 pub fn to_json_value(&self) -> serde_json::Value {
61 serde_json::json!({ "sha256": self.sha256 })
62 }
63
64 #[must_use]
69 pub fn to_canonical_json_value(&self) -> canonical_json::CanonicalJsonValue {
70 #[expect(
71 clippy::expect_used,
72 reason = "We know this will succeed, because we know what the hashes object we're constructing looks like"
73 )]
74 canonical_json::to_canonical_value(self.to_json_value()).expect("We know this will succeed, because we know what the hashes object we're constructing looks like")
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
80pub struct HashedEventRef {
81 pub(crate) event_id: OwnedEventId,
83 pub(crate) hashes: EventHash,
85}
86
87impl HashedEventRef {
88 #[must_use]
90 pub fn new(event_id: OwnedEventId, hashes: EventHash) -> Self {
91 Self { event_id, hashes }
92 }
93
94 #[must_use]
96 pub fn event_id(&self) -> &EventId {
97 &self.event_id
98 }
99
100 #[must_use]
102 pub const fn hashes(&self) -> &EventHash {
103 &self.hashes
104 }
105}
106
107#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
109#[serde(untagged)]
110pub enum EventRefs {
111 Hashed(Vec<HashedEventRef>),
114 Plain(Vec<OwnedEventId>),
116 Empty,
118}
119
120impl From<&EventRefs> for Vec<OwnedEventId> {
121 fn from(value: &EventRefs) -> Self {
122 value.event_ids().map(ToOwned::to_owned).collect()
123 }
124}
125
126impl EventRefs {
127 #[must_use]
129 pub fn event_ids(&self) -> Box<dyn Iterator<Item = &OwnedEventId> + Send + Sync + '_> {
130 match self {
131 Self::Empty => Box::new(iter::empty()),
132 Self::Hashed(references) => {
133 Box::new(references.iter().map(|reference| &reference.event_id))
134 }
135 Self::Plain(ids) => Box::new(ids.iter()),
136 }
137 }
138
139 pub(crate) fn pdu_deserialize<'de, D>(deserializer: D) -> Result<Self, D::Error>
141 where
142 D: Deserializer<'de>,
143 {
144 let event_refs = EventRefs::deserialize(deserializer)?;
145 if event_refs.is_empty() { Ok(EventRefs::Empty) } else { Ok(event_refs) }
146 }
147
148 pub(crate) fn pdu_serialize(&self) -> serde_json::Value {
150 match self {
151 Self::Empty => serde_json::Value::Array(Vec::new()),
152 EventRefs::Hashed(events) => {
153 serde_json::Value::Array(events.iter().map(serialize_hashed_event_ref).collect())
154 }
155 EventRefs::Plain(events) => serde_json::Value::Array(
156 events
157 .iter()
158 .map(|event_id| serde_json::Value::String(event_id.to_string()))
159 .collect(),
160 ),
161 }
162 }
163
164 #[must_use]
166 pub const fn len(&self) -> usize {
167 match self {
168 Self::Empty => 0,
169 Self::Hashed(refs) => refs.len(),
170 Self::Plain(ids) => ids.len(),
171 }
172 }
173
174 #[must_use]
176 pub const fn is_empty(&self) -> bool {
177 self.len() == 0
178 }
179}
180
181impl From<Vec<OwnedEventId>> for EventRefs {
182 fn from(events: Vec<OwnedEventId>) -> Self {
183 Self::Plain(events)
184 }
185}
186
187#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Copy)]
190pub enum PartsVerified {
191 CompleteEvent,
194 OnlySignatures,
197}
198
199impl From<ruma::signatures::Verified> for PartsVerified {
200 fn from(value: ruma::signatures::Verified) -> Self {
201 match value {
202 ruma::signatures::Verified::All => PartsVerified::CompleteEvent,
203 ruma::signatures::Verified::Signatures => PartsVerified::OnlySignatures,
204 }
205 }
206}
207
208#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
210pub struct VerificationMetadata {
211 pub attempted_at: MilliSecondsSinceUnixEpoch,
213 pub signature_verification: PartsVerified,
215 pub room_version: KnownRoomVersionId,
217 pub soft_failed: bool,
228}
229
230#[cfg(any(test, feature = "test_utils"))]
231impl VerificationMetadata {
232 #[must_use]
235 pub fn example(room_version: KnownRoomVersionId) -> Self {
236 Self {
237 attempted_at: MilliSecondsSinceUnixEpoch::now(),
238 signature_verification: PartsVerified::CompleteEvent,
239 room_version,
240 soft_failed: false,
241 }
242 }
243}
244
245#[derive(Copy, Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
252#[allow(
253 clippy::missing_docs_in_private_items,
254 missing_docs,
255 reason = "The name of the room version is documentation enough"
256)]
257#[derive(Default)]
258pub enum KnownRoomVersionId {
259 V1,
260 V2,
261 V3,
262 V4,
263 V5,
264 V6,
265 V7,
266 V8,
267 V9,
268 V10,
269 #[default]
270 V11,
271 V12,
272}
273
274pub const SUPPORTED_ROOM_VERSIONS: [KnownRoomVersionId; 10] = [
276 KnownRoomVersionId::V3,
277 KnownRoomVersionId::V4,
278 KnownRoomVersionId::V5,
279 KnownRoomVersionId::V6,
280 KnownRoomVersionId::V7,
281 KnownRoomVersionId::V8,
282 KnownRoomVersionId::V9,
283 KnownRoomVersionId::V10,
284 KnownRoomVersionId::V11,
285 KnownRoomVersionId::V12,
286];
287
288impl std::fmt::Display for KnownRoomVersionId {
289 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290 let room_version_id: ruma::RoomVersionId = (*self).into();
291 room_version_id.fmt(f)
292 }
293}
294
295impl TryFrom<ruma::RoomVersionId> for KnownRoomVersionId {
296 type Error = UnknownRoomVersionError;
297
298 fn try_from(value: ruma::RoomVersionId) -> Result<Self, Self::Error> {
299 match value {
302 ruma::RoomVersionId::V1 => Ok(KnownRoomVersionId::V1),
303 ruma::RoomVersionId::V2 => Ok(KnownRoomVersionId::V2),
304 ruma::RoomVersionId::V3 => Ok(KnownRoomVersionId::V3),
305 ruma::RoomVersionId::V4 => Ok(KnownRoomVersionId::V4),
306 ruma::RoomVersionId::V5 => Ok(KnownRoomVersionId::V5),
307 ruma::RoomVersionId::V6 => Ok(KnownRoomVersionId::V6),
308 ruma::RoomVersionId::V7 => Ok(KnownRoomVersionId::V7),
309 ruma::RoomVersionId::V8 => Ok(KnownRoomVersionId::V8),
310 ruma::RoomVersionId::V9 => Ok(KnownRoomVersionId::V9),
311 ruma::RoomVersionId::V10 => Ok(KnownRoomVersionId::V10),
312 ruma::RoomVersionId::V11 => Ok(KnownRoomVersionId::V11),
313 ruma::RoomVersionId::V12 => Ok(KnownRoomVersionId::V12),
314 _ => Err(UnknownRoomVersionError(value)),
315 }
316 }
317}
318
319impl From<KnownRoomVersionId> for ruma::RoomVersionId {
320 fn from(value: KnownRoomVersionId) -> Self {
321 match value {
322 KnownRoomVersionId::V1 => ruma::RoomVersionId::V1,
323 KnownRoomVersionId::V2 => ruma::RoomVersionId::V2,
324 KnownRoomVersionId::V3 => ruma::RoomVersionId::V3,
325 KnownRoomVersionId::V4 => ruma::RoomVersionId::V4,
326 KnownRoomVersionId::V5 => ruma::RoomVersionId::V5,
327 KnownRoomVersionId::V6 => ruma::RoomVersionId::V6,
328 KnownRoomVersionId::V7 => ruma::RoomVersionId::V7,
329 KnownRoomVersionId::V8 => ruma::RoomVersionId::V8,
330 KnownRoomVersionId::V9 => ruma::RoomVersionId::V9,
331 KnownRoomVersionId::V10 => ruma::RoomVersionId::V10,
332 KnownRoomVersionId::V11 => ruma::RoomVersionId::V11,
333 KnownRoomVersionId::V12 => ruma::RoomVersionId::V12,
334 }
335 }
336}
337
338impl FromStr for KnownRoomVersionId {
339 type Err = UnknownRoomVersionStringError;
340
341 fn from_str(value: &str) -> Result<Self, Self::Err> {
342 match value {
343 "1" => Ok(KnownRoomVersionId::V1),
344 "2" => Ok(KnownRoomVersionId::V2),
345 "3" => Ok(KnownRoomVersionId::V3),
346 "4" => Ok(KnownRoomVersionId::V4),
347 "5" => Ok(KnownRoomVersionId::V5),
348 "6" => Ok(KnownRoomVersionId::V6),
349 "7" => Ok(KnownRoomVersionId::V7),
350 "8" => Ok(KnownRoomVersionId::V8),
351 "9" => Ok(KnownRoomVersionId::V9),
352 "10" => Ok(KnownRoomVersionId::V10),
353 "11" => Ok(KnownRoomVersionId::V11),
354 "12" => Ok(KnownRoomVersionId::V12),
355 _ => Err(UnknownRoomVersionStringError(value.to_owned())),
356 }
357 }
358}
359
360impl KnownRoomVersionId {
361 #[must_use]
363 pub fn rules(&self) -> RoomVersionRules {
364 match self {
365 KnownRoomVersionId::V1 => RoomVersionRules::V1,
366 KnownRoomVersionId::V2 => RoomVersionRules::V2,
367 KnownRoomVersionId::V3 => RoomVersionRules::V3,
368 KnownRoomVersionId::V4 => RoomVersionRules::V4,
369 KnownRoomVersionId::V5 => RoomVersionRules::V5,
370 KnownRoomVersionId::V6 => RoomVersionRules::V6,
371 KnownRoomVersionId::V7 => RoomVersionRules::V7,
372 KnownRoomVersionId::V8 => RoomVersionRules::V8,
373 KnownRoomVersionId::V9 => RoomVersionRules::V9,
374 KnownRoomVersionId::V10 => RoomVersionRules::V10,
375 KnownRoomVersionId::V11 => RoomVersionRules::V11,
376 KnownRoomVersionId::V12 => RoomVersionRules::V12,
377 }
378 }
379
380 #[must_use]
384 pub(crate) const fn events_reference_format(self) -> KnownEventsReferenceFormatVersion {
385 match self {
386 KnownRoomVersionId::V1 | KnownRoomVersionId::V2 => {
387 KnownEventsReferenceFormatVersion::V1
388 }
389 KnownRoomVersionId::V3
390 | KnownRoomVersionId::V4
391 | KnownRoomVersionId::V5
392 | KnownRoomVersionId::V6
393 | KnownRoomVersionId::V7
394 | KnownRoomVersionId::V8
395 | KnownRoomVersionId::V9
396 | KnownRoomVersionId::V10
397 | KnownRoomVersionId::V11
398 | KnownRoomVersionId::V12 => KnownEventsReferenceFormatVersion::V2,
399 }
400 }
401}
402
403#[derive(Copy, Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
410#[allow(
411 clippy::missing_docs_in_private_items,
412 missing_docs,
413 reason = "The name of the events reference format version is enough"
414)]
415pub enum KnownEventsReferenceFormatVersion {
416 V1,
417 V2,
418}
419
420impl From<KnownEventsReferenceFormatVersion> for EventsReferenceFormatVersion {
421 fn from(val: KnownEventsReferenceFormatVersion) -> Self {
422 match val {
423 KnownEventsReferenceFormatVersion::V1 => EventsReferenceFormatVersion::V1,
424 KnownEventsReferenceFormatVersion::V2 => EventsReferenceFormatVersion::V2,
425 }
426 }
427}
428
429#[derive(Debug, Clone, Default)]
431pub struct ClientContext {
432 pub transaction_id: Option<OwnedTransactionId>,
434 pub prev_content: Option<serde_json::Value>,
436 pub age: Option<u64>,
438}
439
440#[derive(Debug, Clone, Copy)]
442#[non_exhaustive]
443pub enum PduFormat {
444 V1,
446 V3,
448 V6,
450 V12,
452}
453
454impl From<KnownRoomVersionId> for PduFormat {
455 fn from(value: KnownRoomVersionId) -> Self {
456 match value {
457 KnownRoomVersionId::V1 | KnownRoomVersionId::V2 => PduFormat::V1,
458 KnownRoomVersionId::V3 | KnownRoomVersionId::V4 | KnownRoomVersionId::V5 => {
459 PduFormat::V3
460 }
461 KnownRoomVersionId::V6
462 | KnownRoomVersionId::V7
463 | KnownRoomVersionId::V8
464 | KnownRoomVersionId::V9
465 | KnownRoomVersionId::V10
466 | KnownRoomVersionId::V11 => PduFormat::V6,
467 KnownRoomVersionId::V12 => PduFormat::V12,
468 }
469 }
470}
471
472#[derive(Debug, Clone, Copy)]
474pub struct EventFormatOptions {
475 pub events_reference_format: EventsReferenceFormatVersion,
477 pub require_event_id: bool,
479 pub require_room_create_room_id: bool,
481 pub content_field_redacts: bool,
487}
488
489impl EventFormatOptions {
490 #[must_use]
492 pub const fn from_room_version(room_version: &RoomVersionRules) -> Self {
493 Self {
494 events_reference_format: room_version.events_reference_format,
495 require_event_id: room_version.event_format.require_event_id,
496 require_room_create_room_id: room_version.event_format.require_room_create_room_id,
497 content_field_redacts: room_version.redaction.content_field_redacts,
498 }
499 }
500}
501
502#[cfg(test)]
503mod tests;