Skip to main content

matrix_sdk_indexeddb/event_cache_store/
types.rs

1// Copyright 2025 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License
14
15use std::time::Duration;
16
17use matrix_sdk_base::{
18    cross_process_lock::CrossProcessLockGeneration,
19    deserialized_responses::TimelineEvent,
20    event_cache::store::extract_event_relation,
21    linked_chunk::{ChunkIdentifier, LinkedChunkId, OwnedLinkedChunkId},
22};
23use ruma::{EventId, OwnedEventId, RoomId};
24use serde::{Deserialize, Serialize};
25
26/// Representation of a time-based lock on the entire
27/// [`IndexeddbEventCacheStore`](crate::event_cache_store::IndexeddbEventCacheStore)
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct Lease {
30    pub key: String,
31    pub holder: String,
32    pub expiration: Duration,
33    pub generation: CrossProcessLockGeneration,
34}
35
36/// Representation of a [`Chunk`](matrix_sdk_base::linked_chunk::Chunk)
37/// which can be stored in IndexedDB.
38#[derive(Debug, Serialize, Deserialize)]
39pub struct Chunk {
40    /// The linked chunk id in which the chunk exists.
41    pub linked_chunk_id: OwnedLinkedChunkId,
42    /// The identifier of the chunk - i.e.,
43    /// [`ChunkIdentifier`](matrix_sdk_base::linked_chunk::ChunkIdentifier).
44    pub identifier: u64,
45    /// The previous chunk in the list.
46    pub previous: Option<u64>,
47    /// The next chunk in the list.
48    pub next: Option<u64>,
49    /// The type of the chunk.
50    pub chunk_type: ChunkType,
51}
52
53/// The type of a [`Chunk`](Chunk)
54#[derive(Debug, Serialize, Deserialize)]
55#[serde(rename_all = "snake_case")]
56pub enum ChunkType {
57    /// A chunk that holds events.
58    Event,
59    /// A chunk that represents a gap.
60    Gap,
61}
62
63/// An inclusive representation of an
64/// [`Event`](matrix_sdk_base::event_cache::Event) which can be stored in
65/// IndexedDB.
66///
67/// This is useful when (de)serializing an event which may either be in-band or
68/// out-of-band.
69#[derive(Debug, Serialize, Deserialize)]
70#[serde(untagged)]
71pub enum Event {
72    /// An in-band event, i.e., an event which is part of a chunk.
73    InBand(InBandEvent),
74    /// An out-of-band event, i.e., an event which is not part of a chunk.
75    OutOfBand(OutOfBandEvent),
76}
77
78impl From<Event> for TimelineEvent {
79    fn from(value: Event) -> Self {
80        match value {
81            Event::InBand(e) => e.content,
82            Event::OutOfBand(e) => e.content,
83        }
84    }
85}
86
87impl Event {
88    /// The [`LinkedChunkId`] in which the underlying event exists.
89    pub fn linked_chunk_id(&self) -> LinkedChunkId<'_> {
90        match self {
91            Event::InBand(e) => e.linked_chunk_id.as_ref(),
92            Event::OutOfBand(e) => e.linked_chunk_id.as_ref(),
93        }
94    }
95
96    /// The [`RoomId`] of the room in which the underlying event exists.
97    pub fn room_id(&self) -> &RoomId {
98        match self {
99            Event::InBand(e) => e.room_id(),
100            Event::OutOfBand(e) => e.room_id(),
101        }
102    }
103
104    /// The [`EventId`] of the underlying event.
105    pub fn event_id(&self) -> Option<&EventId> {
106        match self {
107            Event::InBand(e) => e.event_id(),
108            Event::OutOfBand(e) => e.event_id(),
109        }
110    }
111
112    /// The [`Position`] of the underlying event, if it is in a chunk.
113    pub fn position(&self) -> Option<Position> {
114        match self {
115            Event::InBand(e) => Some(e.position),
116            Event::OutOfBand(_) => None,
117        }
118    }
119
120    /// The [`OwnedEventId`] and
121    /// [`RelationType`](ruma::events::relation::RelationType) of the underlying
122    /// event as a [`String`].
123    pub fn relation(&self) -> Option<(OwnedEventId, String)> {
124        match self {
125            Event::InBand(e) => e.relation(),
126            Event::OutOfBand(e) => e.relation(),
127        }
128    }
129
130    /// Sets the content of the underlying [`GenericEvent`] and returns
131    /// the mutated [`Event`]
132    pub fn with_content(mut self, content: TimelineEvent) -> Self {
133        match self {
134            Event::InBand(ref mut i) => i.content = content,
135            Event::OutOfBand(ref mut o) => o.content = content,
136        }
137        self
138    }
139
140    /// Ensures that the underlying [`GenericEvent`] is an [`OutOfBandEvent`].
141    /// If it is not an [`OutOfBandEvent`], then it is converted into one.
142    pub fn into_out_of_band_event(self) -> Self {
143        match self {
144            Event::InBand(i) => Self::OutOfBand(i.into()),
145            o @ Event::OutOfBand(_) => o,
146        }
147    }
148}
149
150/// A generic representation of an
151/// [`Event`](matrix_sdk_base::event_cache::Event) which can be stored in
152/// IndexedDB.
153///
154/// This is useful when (de)serializing an event which is required to be either
155/// in-band or out-of-band.
156#[derive(Debug, Serialize, Deserialize)]
157pub struct GenericEvent<P> {
158    /// The linked chunk id in which the event exists.
159    pub linked_chunk_id: OwnedLinkedChunkId,
160    /// The full content of the event.
161    pub content: TimelineEvent,
162    /// The position of the event, if it is in a chunk.
163    pub position: P,
164}
165
166impl<P> GenericEvent<P> {
167    /// The [`RoomId`] of the room in which the event exists.
168    pub fn room_id(&self) -> &RoomId {
169        self.linked_chunk_id.room_id()
170    }
171
172    /// The [`EventId`] of the underlying event.
173    pub fn event_id(&self) -> Option<&EventId> {
174        self.content.event_id()
175    }
176
177    /// The event that the underlying event relates to, if any.
178    ///
179    /// Returns the related [`OwnedEventId`] and the
180    /// [`RelationType`](ruma::events::relation::RelationType) as a [`String`].
181    pub fn relation(&self) -> Option<(OwnedEventId, String)> {
182        extract_event_relation(self.content.raw())
183    }
184}
185
186/// A concrete instance of [`GenericEvent`] for in-band events, i.e.,
187/// events which are part of a chunk and therefore have a position.
188pub type InBandEvent = GenericEvent<Position>;
189
190/// A concrete instance of [`GenericEvent`] for out-of-band events, i.e.,
191/// events which are not part of a chunk and therefore have no position.
192pub type OutOfBandEvent = GenericEvent<()>;
193
194impl From<InBandEvent> for OutOfBandEvent {
195    fn from(value: InBandEvent) -> Self {
196        Self { linked_chunk_id: value.linked_chunk_id, content: value.content, position: () }
197    }
198}
199
200/// A representation of [`Position`](matrix_sdk_base::linked_chunk::Position)
201/// which can be stored in IndexedDB.
202#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
203pub struct Position {
204    /// The identifier of the chunk.
205    pub chunk_identifier: u64,
206    /// The index of the event within the chunk.
207    pub index: usize,
208}
209
210impl From<Position> for matrix_sdk_base::linked_chunk::Position {
211    fn from(value: Position) -> Self {
212        Self::new(ChunkIdentifier::new(value.chunk_identifier), value.index)
213    }
214}
215
216impl From<matrix_sdk_base::linked_chunk::Position> for Position {
217    fn from(value: matrix_sdk_base::linked_chunk::Position) -> Self {
218        Self { chunk_identifier: value.chunk_identifier().index(), index: value.index() }
219    }
220}
221
222/// A representation of [`Gap`](matrix_sdk_base::linked_chunk::Gap)
223/// which can be stored in IndexedDB.
224#[derive(Debug, Serialize, Deserialize)]
225pub struct Gap {
226    /// The linked chunk id in which the gap exists.
227    pub linked_chunk_id: OwnedLinkedChunkId,
228    /// The identifier of the chunk containing this gap.
229    pub chunk_identifier: u64,
230    /// The token to use in the query, extracted from a previous "from" /
231    /// "end" field of a `/messages` response.
232    #[serde(alias = "prev_token")]
233    pub token: String,
234}