1use std::ops::Deref;
16
17use indexed_db_futures::{cursor::CursorDirection, transaction as inner};
18use matrix_sdk_base::media::{
19 MediaRequestParameters,
20 store::{IgnoreMediaRetentionPolicy, MediaRetentionPolicy},
21};
22use ruma::MxcUri;
23use uuid::Uuid;
24
25use crate::{
26 media_store::{
27 serializer::indexed_types::{
28 IndexedCoreIdKey, IndexedLease, IndexedLeaseIdKey, IndexedMediaCleanupTime,
29 IndexedMediaContent, IndexedMediaContentIdKey, IndexedMediaMetadata,
30 IndexedMediaMetadataContentSizeKey, IndexedMediaMetadataIdKey,
31 IndexedMediaMetadataLastAccessKey, IndexedMediaMetadataRetentionKey,
32 IndexedMediaMetadataUriKey,
33 },
34 types::{Lease, Media, MediaCleanupTime, MediaContent, MediaMetadata, UnixTime},
35 },
36 serializer::indexed_type::{
37 IndexedTypeSerializer, range::IndexedKeyRange, traits::IndexedPrefixKeyComponentBounds,
38 },
39 transaction::{Transaction, TransactionError},
40};
41
42pub struct IndexeddbMediaStoreTransaction<'a> {
46 transaction: Transaction<'a>,
47}
48
49impl<'a> Deref for IndexeddbMediaStoreTransaction<'a> {
50 type Target = Transaction<'a>;
51
52 fn deref(&self) -> &Self::Target {
53 &self.transaction
54 }
55}
56
57impl<'a> IndexeddbMediaStoreTransaction<'a> {
58 pub fn new(transaction: inner::Transaction<'a>, serializer: &'a IndexedTypeSerializer) -> Self {
59 Self { transaction: Transaction::new(transaction, serializer) }
60 }
61
62 pub fn into_inner(self) -> Transaction<'a> {
64 self.transaction
65 }
66
67 pub async fn commit(self) -> Result<(), TransactionError> {
69 self.transaction.commit().await
70 }
71
72 pub async fn get_lease_by_id(&self, id: &str) -> Result<Option<Lease>, TransactionError> {
75 self.transaction.get_item_by_key_components::<Lease, IndexedLeaseIdKey>(id).await
76 }
77
78 pub async fn put_lease(&self, lease: &Lease) -> Result<IndexedLease, TransactionError> {
83 self.transaction.put_item(lease).await
84 }
85
86 pub async fn get_media_retention_policy(
88 &self,
89 ) -> Result<Option<MediaRetentionPolicy>, TransactionError> {
90 self.transaction
91 .get_item_by_key_components::<MediaRetentionPolicy, IndexedCoreIdKey>(())
92 .await
93 }
94
95 pub async fn get_media_cleanup_time(
97 &self,
98 ) -> Result<Option<MediaCleanupTime>, TransactionError> {
99 self.transaction.get_item_by_key_components::<MediaCleanupTime, IndexedCoreIdKey>(()).await
100 }
101
102 pub async fn put_media_cleanup_time(
107 &self,
108 time: impl Into<MediaCleanupTime>,
109 ) -> Result<IndexedMediaCleanupTime, TransactionError> {
110 let time: MediaCleanupTime = time.into();
111 self.transaction.put_item(&time).await
112 }
113
114 pub async fn access_media_by_id(
119 &self,
120 request_parameters: &MediaRequestParameters,
121 current_time: impl Into<UnixTime>,
122 ) -> Result<Option<Media>, TransactionError> {
123 if let Some(metadata) =
124 self.access_media_metadata_by_id(request_parameters, current_time).await?
125 {
126 let content = self
127 .get_media_content_by_id(metadata.content_id)
128 .await?
129 .ok_or(TransactionError::ItemNotFound)?;
130 Ok(Some(Media {
131 request_parameters: metadata.request_parameters,
132 last_access: metadata.last_access,
133 ignore_policy: metadata.ignore_policy,
134 content: content.data,
135 }))
136 } else {
137 Ok(None)
138 }
139 }
140
141 pub async fn access_media_by_uri(
146 &self,
147 uri: &MxcUri,
148 current_time: impl Into<UnixTime>,
149 ) -> Result<Vec<Media>, TransactionError> {
150 let mut medias = Vec::new();
151 for metadata in self.access_media_metadata_by_uri(uri, current_time).await? {
152 let content = self
153 .get_media_content_by_id(metadata.content_id)
154 .await?
155 .ok_or(TransactionError::ItemNotFound)?;
156 medias.push(Media {
157 request_parameters: metadata.request_parameters,
158 last_access: metadata.last_access,
159 ignore_policy: metadata.ignore_policy,
160 content: content.data,
161 });
162 }
163 Ok(medias)
164 }
165
166 pub async fn get_cache_size(
174 &self,
175 ignore_policy: IgnoreMediaRetentionPolicy,
176 ) -> Result<Option<usize>, TransactionError> {
177 Ok(self
178 .get_all_media_metadata_keys_by_content_size(ignore_policy)
179 .await?
180 .iter()
181 .try_fold(0usize, |size, key| size.checked_add(key.content_size())))
182 }
183
184 pub async fn put_media_if_policy_compliant(
192 &self,
193 media: Media,
194 policy: MediaRetentionPolicy,
195 ) -> Result<Option<(IndexedMediaMetadata, IndexedMediaContent)>, TransactionError> {
196 let content_id = match self.get_media_metadata_by_id(&media.request_parameters).await? {
197 Some(metadata) => metadata.content_id,
198 None => Uuid::new_v4(),
199 };
200 let content = MediaContent { content_id, data: media.content };
201 let option = if media.ignore_policy.is_yes() {
202 self.put_media_content(&content).await.map(Some)?
203 } else {
204 self.put_media_content_if_policy_compliant(&content, policy).await?
205 };
206 if let Some(indexed_content) = option {
207 let indexed_metadata = self
208 .put_media_metadata(&MediaMetadata {
209 request_parameters: media.request_parameters,
210 last_access: media.last_access,
211 ignore_policy: media.ignore_policy,
212 content_id,
213 content_size: indexed_content.content.len(),
214 })
215 .await?;
216 Ok(Some((indexed_metadata, indexed_content)))
217 } else {
218 Ok(None)
219 }
220 }
221
222 pub async fn delete_media_by_id(
225 &self,
226 request_parameters: &MediaRequestParameters,
227 ) -> Result<(), TransactionError> {
228 if let Some(metadata) = self.get_media_metadata_by_id(request_parameters).await? {
229 self.delete_media_content_by_id(metadata.content_id).await?;
230 }
231 self.delete_media_metadata_by_id(request_parameters).await
232 }
233
234 pub async fn delete_media_by_uri(&self, uri: &MxcUri) -> Result<(), TransactionError> {
237 for metadata in self.get_media_metadata_by_uri(uri).await? {
238 self.delete_media_content_by_id(metadata.content_id).await?;
239 }
240 self.delete_media_metadata_by_uri(uri).await
241 }
242
243 pub async fn delete_media_by_content_size(
247 &self,
248 ignore_policy: IgnoreMediaRetentionPolicy,
249 content_size: impl Into<IndexedKeyRange<usize>>,
250 ) -> Result<(), TransactionError> {
251 let range = content_size.into();
252 for key in self.get_media_metadata_keys_by_content_size(ignore_policy, range).await? {
253 self.delete_media_content_by_id(key.content_id()).await?;
254 }
255 self.delete_media_metadata_by_content_size(ignore_policy, range).await
256 }
257
258 pub async fn delete_media_by_content_size_greater_than(
262 &self,
263 ignore_policy: IgnoreMediaRetentionPolicy,
264 content_size: usize,
265 ) -> Result<(), TransactionError> {
266 let (_, upper, _) =
267 IndexedMediaMetadataContentSizeKey::upper_key_components_with_prefix(ignore_policy);
268 self.delete_media_by_content_size(ignore_policy, (content_size + 1, upper)).await
269 }
270
271 pub async fn delete_media_by_last_access(
275 &self,
276 ignore_policy: IgnoreMediaRetentionPolicy,
277 last_access: impl Into<IndexedKeyRange<UnixTime>>,
278 ) -> Result<(), TransactionError> {
279 let range = last_access.into();
280 for key in self.get_media_metadata_keys_by_last_access(ignore_policy, range).await? {
281 self.delete_media_content_by_id(key.content_id()).await?;
282 }
283 self.delete_media_metadata_by_last_access(ignore_policy, range).await
284 }
285
286 pub async fn delete_media_by_last_access_earlier_than(
290 &self,
291 ignore_policy: IgnoreMediaRetentionPolicy,
292 time: UnixTime,
293 ) -> Result<(), TransactionError> {
294 let (_, lower, _) =
295 IndexedMediaMetadataLastAccessKey::lower_key_components_with_prefix(ignore_policy);
296 self.delete_media_by_last_access(ignore_policy, (lower, time)).await
297 }
298
299 pub async fn delete_media_by_retention_metadata(
303 &self,
304 ignore_policy: IgnoreMediaRetentionPolicy,
305 range: impl Into<IndexedKeyRange<(UnixTime, usize)>>,
306 ) -> Result<(), TransactionError> {
307 let range = range.into();
308 for key in self.get_media_metadata_keys_by_retention(ignore_policy, range).await? {
309 self.delete_media_content_by_id(key.content_id()).await?;
310 }
311 self.delete_media_metadata_by_retention(ignore_policy, range).await
312 }
313
314 pub async fn delete_media_by_retention_metadata_to(
318 &self,
319 ignore_policy: IgnoreMediaRetentionPolicy,
320 last_access: UnixTime,
321 content_size: usize,
322 ) -> Result<(), TransactionError> {
323 let (_, lower_last_access, lower_content_size, _) =
324 IndexedMediaMetadataRetentionKey::lower_key_components_with_prefix(ignore_policy);
325 let lower = (lower_last_access, lower_content_size);
326 self.delete_media_by_retention_metadata(ignore_policy, (lower, (last_access, content_size)))
327 .await
328 }
329
330 pub async fn get_media_metadata_by_id(
334 &self,
335 request_parameters: &MediaRequestParameters,
336 ) -> Result<Option<MediaMetadata>, TransactionError> {
337 self.get_item_by_key_components::<MediaMetadata, IndexedMediaMetadataIdKey>(
338 request_parameters,
339 )
340 .await
341 }
342
343 pub async fn access_media_metadata_by_id(
348 &self,
349 request_parameters: &MediaRequestParameters,
350 current_time: impl Into<UnixTime>,
351 ) -> Result<Option<MediaMetadata>, TransactionError> {
352 if let Some(mut media_metadata) = self.get_media_metadata_by_id(request_parameters).await? {
353 let last_access = media_metadata.last_access;
354 media_metadata.last_access = current_time.into();
355 self.put_item(&media_metadata).await?;
356 media_metadata.last_access = last_access;
357 Ok(Some(media_metadata))
358 } else {
359 Ok(None)
360 }
361 }
362
363 pub async fn get_media_metadata_by_uri(
365 &self,
366 uri: &MxcUri,
367 ) -> Result<Vec<MediaMetadata>, TransactionError> {
368 self.get_items_by_key_components::<MediaMetadata, IndexedMediaMetadataUriKey>(uri).await
369 }
370
371 pub async fn access_media_metadata_by_uri(
376 &self,
377 uri: &MxcUri,
378 current_time: impl Into<UnixTime>,
379 ) -> Result<Vec<MediaMetadata>, TransactionError> {
380 let current_time = current_time.into();
381 let mut media_metadatas = Vec::new();
382 for mut media_metadata in self.get_media_metadata_by_uri(uri).await? {
383 let last_access = media_metadata.last_access;
384 media_metadata.last_access = current_time;
385 self.put_item(&media_metadata).await?;
386 media_metadata.last_access = last_access;
387 media_metadatas.push(media_metadata);
388 }
389 Ok(media_metadatas)
390 }
391
392 pub async fn get_media_metadata_keys_by_content_size(
396 &self,
397 ignore_policy: IgnoreMediaRetentionPolicy,
398 content_size: impl Into<IndexedKeyRange<usize>>,
399 ) -> Result<Vec<IndexedMediaMetadataContentSizeKey>, TransactionError> {
400 let range = Into::<IndexedKeyRange<usize>>::into(content_size)
401 .map(|last_access| (ignore_policy, last_access))
402 .into_prefix(self.serializer().inner());
403 self.get_keys::<MediaMetadata, IndexedMediaMetadataContentSizeKey>(range).await
404 }
405
406 pub async fn get_all_media_metadata_keys_by_content_size(
410 &self,
411 ignore_policy: IgnoreMediaRetentionPolicy,
412 ) -> Result<Vec<IndexedMediaMetadataContentSizeKey>, TransactionError> {
413 let (_, lower, _) =
414 IndexedMediaMetadataContentSizeKey::lower_key_components_with_prefix(ignore_policy);
415 let (_, upper, _) =
416 IndexedMediaMetadataContentSizeKey::upper_key_components_with_prefix(ignore_policy);
417 self.get_media_metadata_keys_by_content_size(ignore_policy, (lower, upper)).await
418 }
419
420 pub async fn get_media_metadata_keys_by_last_access(
424 &self,
425 ignore_policy: IgnoreMediaRetentionPolicy,
426 last_access: impl Into<IndexedKeyRange<UnixTime>>,
427 ) -> Result<Vec<IndexedMediaMetadataLastAccessKey>, TransactionError> {
428 let range = Into::<IndexedKeyRange<UnixTime>>::into(last_access)
429 .map(|last_access| (ignore_policy, last_access))
430 .into_prefix(self.serializer().inner());
431 self.get_keys::<MediaMetadata, IndexedMediaMetadataLastAccessKey>(range).await
432 }
433
434 pub async fn get_media_metadata_keys_by_retention(
439 &self,
440 ignore_policy: IgnoreMediaRetentionPolicy,
441 range: impl Into<IndexedKeyRange<(UnixTime, usize)>>,
442 ) -> Result<Vec<IndexedMediaMetadataRetentionKey>, TransactionError> {
443 let range = Into::<IndexedKeyRange<(UnixTime, usize)>>::into(range)
444 .map(|(last_access, content_size)| (ignore_policy, last_access, content_size))
445 .into_prefix(self.serializer().inner());
446 self.get_keys::<MediaMetadata, IndexedMediaMetadataRetentionKey>(range).await
447 }
448
449 pub async fn fold_media_metadata_keys_by_retention_while<Acc, F>(
462 &self,
463 direction: CursorDirection,
464 ignore_policy: IgnoreMediaRetentionPolicy,
465 init: Acc,
466 f: F,
467 ) -> Result<(Acc, Option<IndexedMediaMetadataRetentionKey>), TransactionError>
468 where
469 F: FnMut(&Acc, &IndexedMediaMetadataRetentionKey) -> Option<Acc>,
470 {
471 self.fold_keys_while::<MediaMetadata, IndexedMediaMetadataRetentionKey, Acc, F>(
472 direction,
473 IndexedKeyRange::all_with_prefix(ignore_policy, self.serializer().inner()),
474 init,
475 f,
476 )
477 .await
478 }
479
480 pub async fn add_media_metadata(
485 &self,
486 media_metadata: &MediaMetadata,
487 ) -> Result<IndexedMediaMetadata, TransactionError> {
488 self.add_item(media_metadata).await
489 }
490
491 pub async fn put_media_metadata(
496 &self,
497 media_metadata: &MediaMetadata,
498 ) -> Result<IndexedMediaMetadata, TransactionError> {
499 self.put_item(media_metadata).await
500 }
501
502 pub async fn delete_media_metadata_by_id(
505 &self,
506 request_parameters: &MediaRequestParameters,
507 ) -> Result<(), TransactionError> {
508 self.delete_item_by_key::<MediaMetadata, IndexedMediaMetadataIdKey>(request_parameters)
509 .await
510 }
511
512 pub async fn delete_media_metadata_by_uri(
515 &self,
516 source: &MxcUri,
517 ) -> Result<(), TransactionError> {
518 self.delete_item_by_key::<MediaMetadata, IndexedMediaMetadataUriKey>(source).await
519 }
520
521 pub async fn delete_media_metadata_by_content_size(
525 &self,
526 ignore_policy: IgnoreMediaRetentionPolicy,
527 content_size: impl Into<IndexedKeyRange<usize>>,
528 ) -> Result<(), TransactionError> {
529 let range = Into::<IndexedKeyRange<usize>>::into(content_size)
530 .map(|size| (ignore_policy, size))
531 .into_prefix(self.serializer().inner());
532 self.delete_items_by_key::<MediaMetadata, IndexedMediaMetadataContentSizeKey>(range).await
533 }
534
535 pub async fn delete_media_metadata_by_content_size_greater_than(
539 &self,
540 ignore_policy: IgnoreMediaRetentionPolicy,
541 content_size: usize,
542 ) -> Result<(), TransactionError> {
543 let (_, upper, _) =
544 IndexedMediaMetadataContentSizeKey::upper_key_components_with_prefix(ignore_policy);
545 self.delete_media_metadata_by_content_size(ignore_policy, (content_size + 1, upper)).await
546 }
547
548 pub async fn delete_media_metadata_by_last_access(
552 &self,
553 ignore_policy: IgnoreMediaRetentionPolicy,
554 last_access: impl Into<IndexedKeyRange<UnixTime>>,
555 ) -> Result<(), TransactionError> {
556 let range = Into::<IndexedKeyRange<UnixTime>>::into(last_access)
557 .map(|last_access| (ignore_policy, last_access))
558 .into_prefix(self.serializer().inner());
559 self.delete_items_by_key::<MediaMetadata, IndexedMediaMetadataLastAccessKey>(range).await
560 }
561
562 pub async fn delete_media_metadata_by_last_access_earlier_than(
566 &self,
567 ignore_policy: IgnoreMediaRetentionPolicy,
568 time: UnixTime,
569 ) -> Result<(), TransactionError> {
570 let (_, lower, _) =
571 IndexedMediaMetadataLastAccessKey::lower_key_components_with_prefix(ignore_policy);
572 self.delete_media_metadata_by_last_access(ignore_policy, (lower, time)).await
573 }
574
575 pub async fn delete_media_metadata_by_retention(
579 &self,
580 ignore_policy: IgnoreMediaRetentionPolicy,
581 range: impl Into<IndexedKeyRange<(UnixTime, usize)>>,
582 ) -> Result<(), TransactionError> {
583 let range = Into::<IndexedKeyRange<(UnixTime, usize)>>::into(range)
584 .map(|(last_access, content_size)| (ignore_policy, last_access, content_size))
585 .into_prefix(self.serializer().inner());
586 self.delete_items_by_key::<MediaMetadata, IndexedMediaMetadataRetentionKey>(range).await
587 }
588
589 pub async fn delete_media_metadata_by_retention_to(
593 &self,
594 ignore_policy: IgnoreMediaRetentionPolicy,
595 last_access: UnixTime,
596 content_size: usize,
597 ) -> Result<(), TransactionError> {
598 let (_, lower_last_access, lower_content_size, _) =
599 IndexedMediaMetadataRetentionKey::lower_key_components_with_prefix(ignore_policy);
600 let lower = (lower_last_access, lower_content_size);
601 self.delete_media_metadata_by_retention(ignore_policy, (lower, (last_access, content_size)))
602 .await
603 }
604
605 pub async fn get_media_content_by_id(
609 &self,
610 id: Uuid,
611 ) -> Result<Option<MediaContent>, TransactionError> {
612 self.get_item_by_key_components::<MediaContent, IndexedMediaContentIdKey>(id).await
613 }
614
615 pub async fn add_media_content(
620 &self,
621 content: &MediaContent,
622 ) -> Result<IndexedMediaContent, TransactionError> {
623 self.add_item(content).await
624 }
625
626 pub async fn put_media_content(
631 &self,
632 content: &MediaContent,
633 ) -> Result<IndexedMediaContent, TransactionError> {
634 self.put_item(content).await
635 }
636
637 pub async fn put_media_content_if_policy_compliant(
644 &self,
645 media: &MediaContent,
646 policy: MediaRetentionPolicy,
647 ) -> Result<Option<IndexedMediaContent>, TransactionError> {
648 self.put_item_if(media, |indexed| {
649 !policy.exceeds_max_file_size(indexed.content.len() as u64)
650 })
651 .await
652 }
653
654 pub async fn delete_media_content_by_id(&self, id: Uuid) -> Result<(), TransactionError> {
656 self.delete_item_by_key::<MediaContent, IndexedMediaContentIdKey>(id).await
657 }
658}