matrix_sdk_indexeddb/event_cache_store/
migrations.rs1use indexed_db_futures::{
16 database::Database,
17 error::{DomException, Error, OpenDbError},
18 transaction::Transaction,
19};
20use thiserror::Error;
21
22pub mod current {
24 use super::{Version, v4};
25
26 pub const VERSION: Version = Version::V4;
27 pub use v4::keys;
28}
29
30#[allow(unused)]
33pub async fn open_and_upgrade_db(name: &str) -> Result<Database, OpenDbError> {
34 Database::open(name)
35 .with_version(current::VERSION as u32)
36 .with_on_upgrade_needed(|event, transaction| {
37 let mut version = Version::try_from(event.old_version() as u32)?;
38 while version < current::VERSION {
39 version = match version.upgrade(transaction)? {
40 Some(next) => next,
41 None => current::VERSION, };
43 }
44 Ok(())
45 })
46 .await
47}
48
49#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
51#[repr(u32)]
52pub enum Version {
53 V0 = 0,
55 V1 = 1,
57 V2 = 2,
59 V3 = 3,
61 V4 = 4,
63}
64
65impl Version {
66 pub fn upgrade(self, transaction: &Transaction<'_>) -> Result<Option<Self>, Error> {
68 match self {
69 Self::V0 => v0::upgrade(transaction).map(Some),
70 Self::V1 => v1::upgrade(transaction).map(Some),
71 Self::V2 => v2::upgrade(transaction).map(Some),
72 Self::V3 => v3::upgrade(transaction).map(Some),
73 Self::V4 => Ok(None),
74 }
75 }
76}
77
78#[derive(Debug, Error)]
79#[error("unknown version: {0}")]
80pub struct UnknownVersionError(u32);
81
82impl TryFrom<u32> for Version {
83 type Error = UnknownVersionError;
84
85 fn try_from(value: u32) -> Result<Self, Self::Error> {
86 match value {
87 0 => Ok(Version::V0),
88 1 => Ok(Version::V1),
89 2 => Ok(Version::V2),
90 3 => Ok(Version::V3),
91 4 => Ok(Version::V4),
92 v => Err(UnknownVersionError(v)),
93 }
94 }
95}
96
97impl From<UnknownVersionError> for Error {
98 fn from(value: UnknownVersionError) -> Self {
99 let message = format!("unknown version: {}", value.0);
100 let name = "UnknownVersionError";
101 match web_sys::DomException::new_with_message_and_name(&message, name) {
102 Ok(inner) => Self::DomException(DomException::DataError(inner)),
103 Err(err) => err.into(),
104 }
105 }
106}
107
108pub mod v0 {
109 use super::*;
110
111 pub fn upgrade(transaction: &Transaction<'_>) -> Result<Version, Error> {
113 v1::create_object_stores(transaction.db())?;
114 Ok(Version::V1)
115 }
116}
117
118pub mod v1 {
119 use indexed_db_futures::Build;
120
121 use super::*;
122
123 pub mod keys {
124 pub const LEASES: &str = "leases";
125 pub const LEASES_KEY_PATH: &str = "id";
126 pub const ROOMS: &str = "rooms";
127 pub const LINKED_CHUNK_IDS: &str = "linked_chunk_ids";
128 pub const LINKED_CHUNKS: &str = "linked_chunks";
129 pub const LINKED_CHUNKS_KEY_PATH: &str = "id";
130 pub const LINKED_CHUNKS_NEXT: &str = "linked_chunks_next";
131 pub const LINKED_CHUNKS_NEXT_KEY_PATH: &str = "next";
132 pub const EVENTS: &str = "events";
133 pub const EVENTS_KEY_PATH: &str = "id";
134 pub const EVENTS_ROOM: &str = "events_room";
135 pub const EVENTS_ROOM_KEY_PATH: &str = "room";
136 pub const EVENTS_POSITION: &str = "events_position";
137 pub const EVENTS_POSITION_KEY_PATH: &str = "position";
138 pub const EVENTS_RELATION: &str = "events_relation";
139 pub const EVENTS_RELATION_KEY_PATH: &str = "relation";
140 pub const EVENTS_RELATION_RELATED_EVENTS: &str = "events_relation_related_event";
141 pub const EVENTS_RELATION_RELATION_TYPES: &str = "events_relation_relation_type";
142 pub const GAPS: &str = "gaps";
143 pub const GAPS_KEY_PATH: &str = "id";
144 }
145
146 pub fn create_object_stores(db: &Database) -> Result<(), Error> {
148 create_lease_object_store(db)?;
149 create_linked_chunks_object_store(db)?;
150 create_events_object_store(db)?;
151 create_gaps_object_store(db)?;
152 Ok(())
153 }
154
155 fn create_lease_object_store(db: &Database) -> Result<(), Error> {
157 let _ = db
158 .create_object_store(keys::LEASES)
159 .with_key_path(keys::LEASES_KEY_PATH.into())
160 .build()?;
161 Ok(())
162 }
163
164 fn create_linked_chunks_object_store(db: &Database) -> Result<(), Error> {
169 let _ = db
170 .create_object_store(keys::LINKED_CHUNKS)
171 .with_key_path(keys::LINKED_CHUNKS_KEY_PATH.into())
172 .build()?
173 .create_index(keys::LINKED_CHUNKS_NEXT, keys::LINKED_CHUNKS_NEXT_KEY_PATH.into())
174 .build()?;
175 Ok(())
176 }
177
178 fn create_events_object_store(db: &Database) -> Result<(), Error> {
187 let events = db
188 .create_object_store(keys::EVENTS)
189 .with_key_path(keys::EVENTS_KEY_PATH.into())
190 .build()?;
191 let _ = events
192 .create_index(keys::EVENTS_ROOM, keys::EVENTS_ROOM_KEY_PATH.into())
193 .with_unique(true)
194 .build()?;
195 let _ = events
196 .create_index(keys::EVENTS_POSITION, keys::EVENTS_POSITION_KEY_PATH.into())
197 .with_unique(true)
198 .build()?;
199 let _ = events
200 .create_index(keys::EVENTS_RELATION, keys::EVENTS_RELATION_KEY_PATH.into())
201 .build()?;
202 Ok(())
203 }
204
205 fn create_gaps_object_store(db: &Database) -> Result<(), Error> {
209 let _ =
210 db.create_object_store(keys::GAPS).with_key_path(keys::GAPS_KEY_PATH.into()).build()?;
211 Ok(())
212 }
213
214 pub fn upgrade(transaction: &Transaction<'_>) -> Result<Version, Error> {
216 v2::empty_leases(transaction)?;
217 Ok(Version::V2)
218 }
219}
220
221mod v2 {
222 pub use super::v1::keys;
224 use super::*;
225
226 pub fn empty_leases(transaction: &Transaction<'_>) -> Result<(), Error> {
229 let object_store = transaction.object_store(keys::LEASES)?;
230
231 object_store.clear()?;
233
234 Ok(())
235 }
236
237 pub fn upgrade(transaction: &Transaction<'_>) -> Result<Version, Error> {
239 v3::update_events_object_store(transaction)?;
240 Ok(Version::V3)
241 }
242}
243
244mod v3 {
245 use indexed_db_futures::Build;
246
247 pub use super::v2::keys;
249 use super::*;
250
251 pub fn update_events_object_store(transaction: &Transaction<'_>) -> Result<(), Error> {
262 remove_events_object_store(transaction)?;
263 create_events_object_store(transaction.db())?;
264 Ok(())
265 }
266
267 pub fn remove_events_object_store(transaction: &Transaction<'_>) -> Result<(), Error> {
269 let object_store = transaction.object_store(keys::EVENTS)?;
270 object_store.clear()?;
275 transaction.db().delete_object_store(keys::EVENTS)?;
276 Ok(())
277 }
278
279 pub fn create_events_object_store(db: &Database) -> Result<(), Error> {
288 let events = db
289 .create_object_store(keys::EVENTS)
290 .with_key_path(keys::EVENTS_KEY_PATH.into())
291 .build()?;
292 let _ =
293 events.create_index(keys::EVENTS_ROOM, keys::EVENTS_ROOM_KEY_PATH.into()).build()?;
294 let _ = events
295 .create_index(keys::EVENTS_POSITION, keys::EVENTS_POSITION_KEY_PATH.into())
296 .with_unique(true)
297 .build()?;
298 let _ = events
299 .create_index(keys::EVENTS_RELATION, keys::EVENTS_RELATION_KEY_PATH.into())
300 .build()?;
301 Ok(())
302 }
303
304 pub fn upgrade(transaction: &Transaction<'_>) -> Result<Version, Error> {
306 v4::empty_event_cache(transaction)?;
307 Ok(Version::V4)
308 }
309}
310
311mod v4 {
312 pub use super::v3::keys;
314 use super::*;
315
316 pub fn empty_event_cache(transaction: &Transaction<'_>) -> Result<(), Error> {
324 let linked_chunks = transaction.object_store(keys::LINKED_CHUNKS)?;
325 linked_chunks.clear()?;
326
327 let gaps = transaction.object_store(keys::GAPS)?;
328 gaps.clear()?;
329
330 let events = transaction.object_store(keys::EVENTS)?;
331 events.clear()?;
332
333 Ok(())
334 }
335}