use std::ops::Deref;
use indexed_db_futures::{cursor::CursorDirection, transaction as inner};
use matrix_sdk_base::media::{
MediaRequestParameters,
store::{IgnoreMediaRetentionPolicy, MediaRetentionPolicy},
};
use ruma::MxcUri;
use uuid::Uuid;
use crate::{
media_store::{
serializer::indexed_types::{
IndexedCoreIdKey, IndexedLease, IndexedLeaseIdKey, IndexedMediaCleanupTime,
IndexedMediaContent, IndexedMediaContentIdKey, IndexedMediaMetadata,
IndexedMediaMetadataContentSizeKey, IndexedMediaMetadataIdKey,
IndexedMediaMetadataLastAccessKey, IndexedMediaMetadataRetentionKey,
IndexedMediaMetadataUriKey,
},
types::{Lease, Media, MediaCleanupTime, MediaContent, MediaMetadata, UnixTime},
},
serializer::indexed_type::{
IndexedTypeSerializer, range::IndexedKeyRange, traits::IndexedPrefixKeyComponentBounds,
},
transaction::{Transaction, TransactionError},
};
pub struct IndexeddbMediaStoreTransaction<'a> {
transaction: Transaction<'a>,
}
impl<'a> Deref for IndexeddbMediaStoreTransaction<'a> {
type Target = Transaction<'a>;
fn deref(&self) -> &Self::Target {
&self.transaction
}
}
impl<'a> IndexeddbMediaStoreTransaction<'a> {
pub fn new(transaction: inner::Transaction<'a>, serializer: &'a IndexedTypeSerializer) -> Self {
Self { transaction: Transaction::new(transaction, serializer) }
}
pub fn into_inner(self) -> Transaction<'a> {
self.transaction
}
pub async fn commit(self) -> Result<(), TransactionError> {
self.transaction.commit().await
}
pub async fn get_lease_by_id(&self, id: &str) -> Result<Option<Lease>, TransactionError> {
self.transaction.get_item_by_key_components::<Lease, IndexedLeaseIdKey>(id).await
}
pub async fn put_lease(&self, lease: &Lease) -> Result<IndexedLease, TransactionError> {
self.transaction.put_item(lease).await
}
pub async fn get_media_retention_policy(
&self,
) -> Result<Option<MediaRetentionPolicy>, TransactionError> {
self.transaction
.get_item_by_key_components::<MediaRetentionPolicy, IndexedCoreIdKey>(())
.await
}
pub async fn get_media_cleanup_time(
&self,
) -> Result<Option<MediaCleanupTime>, TransactionError> {
self.transaction.get_item_by_key_components::<MediaCleanupTime, IndexedCoreIdKey>(()).await
}
pub async fn put_media_cleanup_time(
&self,
time: impl Into<MediaCleanupTime>,
) -> Result<IndexedMediaCleanupTime, TransactionError> {
let time: MediaCleanupTime = time.into();
self.transaction.put_item(&time).await
}
pub async fn access_media_by_id(
&self,
request_parameters: &MediaRequestParameters,
current_time: impl Into<UnixTime>,
) -> Result<Option<Media>, TransactionError> {
if let Some(metadata) =
self.access_media_metadata_by_id(request_parameters, current_time).await?
{
let content = self
.get_media_content_by_id(metadata.content_id)
.await?
.ok_or(TransactionError::ItemNotFound)?;
Ok(Some(Media {
request_parameters: metadata.request_parameters,
last_access: metadata.last_access,
ignore_policy: metadata.ignore_policy,
content: content.data,
}))
} else {
Ok(None)
}
}
pub async fn access_media_by_uri(
&self,
uri: &MxcUri,
current_time: impl Into<UnixTime>,
) -> Result<Vec<Media>, TransactionError> {
let mut medias = Vec::new();
for metadata in self.access_media_metadata_by_uri(uri, current_time).await? {
let content = self
.get_media_content_by_id(metadata.content_id)
.await?
.ok_or(TransactionError::ItemNotFound)?;
medias.push(Media {
request_parameters: metadata.request_parameters,
last_access: metadata.last_access,
ignore_policy: metadata.ignore_policy,
content: content.data,
});
}
Ok(medias)
}
pub async fn get_cache_size(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
) -> Result<Option<usize>, TransactionError> {
Ok(self
.get_all_media_metadata_keys_by_content_size(ignore_policy)
.await?
.iter()
.try_fold(0usize, |size, key| size.checked_add(key.content_size())))
}
pub async fn put_media_if_policy_compliant(
&self,
media: Media,
policy: MediaRetentionPolicy,
) -> Result<Option<(IndexedMediaMetadata, IndexedMediaContent)>, TransactionError> {
let content_id = match self.get_media_metadata_by_id(&media.request_parameters).await? {
Some(metadata) => metadata.content_id,
None => Uuid::new_v4(),
};
let content = MediaContent { content_id, data: media.content };
let option = if media.ignore_policy.is_yes() {
self.put_media_content(&content).await.map(Some)?
} else {
self.put_media_content_if_policy_compliant(&content, policy).await?
};
if let Some(indexed_content) = option {
let indexed_metadata = self
.put_media_metadata(&MediaMetadata {
request_parameters: media.request_parameters,
last_access: media.last_access,
ignore_policy: media.ignore_policy,
content_id,
content_size: indexed_content.content.len(),
})
.await?;
Ok(Some((indexed_metadata, indexed_content)))
} else {
Ok(None)
}
}
pub async fn delete_media_by_id(
&self,
request_parameters: &MediaRequestParameters,
) -> Result<(), TransactionError> {
if let Some(metadata) = self.get_media_metadata_by_id(request_parameters).await? {
self.delete_media_content_by_id(metadata.content_id).await?;
}
self.delete_media_metadata_by_id(request_parameters).await
}
pub async fn delete_media_by_uri(&self, uri: &MxcUri) -> Result<(), TransactionError> {
for metadata in self.get_media_metadata_by_uri(uri).await? {
self.delete_media_content_by_id(metadata.content_id).await?;
}
self.delete_media_metadata_by_uri(uri).await
}
pub async fn delete_media_by_content_size(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
content_size: impl Into<IndexedKeyRange<usize>>,
) -> Result<(), TransactionError> {
let range = content_size.into();
for key in self.get_media_metadata_keys_by_content_size(ignore_policy, range).await? {
self.delete_media_content_by_id(key.content_id()).await?;
}
self.delete_media_metadata_by_content_size(ignore_policy, range).await
}
pub async fn delete_media_by_content_size_greater_than(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
content_size: usize,
) -> Result<(), TransactionError> {
let (_, upper, _) =
IndexedMediaMetadataContentSizeKey::upper_key_components_with_prefix(ignore_policy);
self.delete_media_by_content_size(ignore_policy, (content_size + 1, upper)).await
}
pub async fn delete_media_by_last_access(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
last_access: impl Into<IndexedKeyRange<UnixTime>>,
) -> Result<(), TransactionError> {
let range = last_access.into();
for key in self.get_media_metadata_keys_by_last_access(ignore_policy, range).await? {
self.delete_media_content_by_id(key.content_id()).await?;
}
self.delete_media_metadata_by_last_access(ignore_policy, range).await
}
pub async fn delete_media_by_last_access_earlier_than(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
time: UnixTime,
) -> Result<(), TransactionError> {
let (_, lower, _) =
IndexedMediaMetadataLastAccessKey::lower_key_components_with_prefix(ignore_policy);
self.delete_media_by_last_access(ignore_policy, (lower, time)).await
}
pub async fn delete_media_by_retention_metadata(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
range: impl Into<IndexedKeyRange<(UnixTime, usize)>>,
) -> Result<(), TransactionError> {
let range = range.into();
for key in self.get_media_metadata_keys_by_retention(ignore_policy, range).await? {
self.delete_media_content_by_id(key.content_id()).await?;
}
self.delete_media_metadata_by_retention(ignore_policy, range).await
}
pub async fn delete_media_by_retention_metadata_to(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
last_access: UnixTime,
content_size: usize,
) -> Result<(), TransactionError> {
let (_, lower_last_access, lower_content_size, _) =
IndexedMediaMetadataRetentionKey::lower_key_components_with_prefix(ignore_policy);
let lower = (lower_last_access, lower_content_size);
self.delete_media_by_retention_metadata(ignore_policy, (lower, (last_access, content_size)))
.await
}
pub async fn get_media_metadata_by_id(
&self,
request_parameters: &MediaRequestParameters,
) -> Result<Option<MediaMetadata>, TransactionError> {
self.get_item_by_key_components::<MediaMetadata, IndexedMediaMetadataIdKey>(
request_parameters,
)
.await
}
pub async fn access_media_metadata_by_id(
&self,
request_parameters: &MediaRequestParameters,
current_time: impl Into<UnixTime>,
) -> Result<Option<MediaMetadata>, TransactionError> {
if let Some(mut media_metadata) = self.get_media_metadata_by_id(request_parameters).await? {
let last_access = media_metadata.last_access;
media_metadata.last_access = current_time.into();
self.put_item(&media_metadata).await?;
media_metadata.last_access = last_access;
Ok(Some(media_metadata))
} else {
Ok(None)
}
}
pub async fn get_media_metadata_by_uri(
&self,
uri: &MxcUri,
) -> Result<Vec<MediaMetadata>, TransactionError> {
self.get_items_by_key_components::<MediaMetadata, IndexedMediaMetadataUriKey>(uri).await
}
pub async fn access_media_metadata_by_uri(
&self,
uri: &MxcUri,
current_time: impl Into<UnixTime>,
) -> Result<Vec<MediaMetadata>, TransactionError> {
let current_time = current_time.into();
let mut media_metadatas = Vec::new();
for mut media_metadata in self.get_media_metadata_by_uri(uri).await? {
let last_access = media_metadata.last_access;
media_metadata.last_access = current_time;
self.put_item(&media_metadata).await?;
media_metadata.last_access = last_access;
media_metadatas.push(media_metadata);
}
Ok(media_metadatas)
}
pub async fn get_media_metadata_keys_by_content_size(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
content_size: impl Into<IndexedKeyRange<usize>>,
) -> Result<Vec<IndexedMediaMetadataContentSizeKey>, TransactionError> {
let range = Into::<IndexedKeyRange<usize>>::into(content_size)
.map(|last_access| (ignore_policy, last_access))
.into_prefix(self.serializer().inner());
self.get_keys::<MediaMetadata, IndexedMediaMetadataContentSizeKey>(range).await
}
pub async fn get_all_media_metadata_keys_by_content_size(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
) -> Result<Vec<IndexedMediaMetadataContentSizeKey>, TransactionError> {
let (_, lower, _) =
IndexedMediaMetadataContentSizeKey::lower_key_components_with_prefix(ignore_policy);
let (_, upper, _) =
IndexedMediaMetadataContentSizeKey::upper_key_components_with_prefix(ignore_policy);
self.get_media_metadata_keys_by_content_size(ignore_policy, (lower, upper)).await
}
pub async fn get_media_metadata_keys_by_last_access(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
last_access: impl Into<IndexedKeyRange<UnixTime>>,
) -> Result<Vec<IndexedMediaMetadataLastAccessKey>, TransactionError> {
let range = Into::<IndexedKeyRange<UnixTime>>::into(last_access)
.map(|last_access| (ignore_policy, last_access))
.into_prefix(self.serializer().inner());
self.get_keys::<MediaMetadata, IndexedMediaMetadataLastAccessKey>(range).await
}
pub async fn get_media_metadata_keys_by_retention(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
range: impl Into<IndexedKeyRange<(UnixTime, usize)>>,
) -> Result<Vec<IndexedMediaMetadataRetentionKey>, TransactionError> {
let range = Into::<IndexedKeyRange<(UnixTime, usize)>>::into(range)
.map(|(last_access, content_size)| (ignore_policy, last_access, content_size))
.into_prefix(self.serializer().inner());
self.get_keys::<MediaMetadata, IndexedMediaMetadataRetentionKey>(range).await
}
pub async fn fold_media_metadata_keys_by_retention_while<Acc, F>(
&self,
direction: CursorDirection,
ignore_policy: IgnoreMediaRetentionPolicy,
init: Acc,
f: F,
) -> Result<(Acc, Option<IndexedMediaMetadataRetentionKey>), TransactionError>
where
F: FnMut(&Acc, &IndexedMediaMetadataRetentionKey) -> Option<Acc>,
{
self.fold_keys_while::<MediaMetadata, IndexedMediaMetadataRetentionKey, Acc, F>(
direction,
IndexedKeyRange::all_with_prefix(ignore_policy, self.serializer().inner()),
init,
f,
)
.await
}
pub async fn add_media_metadata(
&self,
media_metadata: &MediaMetadata,
) -> Result<IndexedMediaMetadata, TransactionError> {
self.add_item(media_metadata).await
}
pub async fn put_media_metadata(
&self,
media_metadata: &MediaMetadata,
) -> Result<IndexedMediaMetadata, TransactionError> {
self.put_item(media_metadata).await
}
pub async fn delete_media_metadata_by_id(
&self,
request_parameters: &MediaRequestParameters,
) -> Result<(), TransactionError> {
self.delete_item_by_key::<MediaMetadata, IndexedMediaMetadataIdKey>(request_parameters)
.await
}
pub async fn delete_media_metadata_by_uri(
&self,
source: &MxcUri,
) -> Result<(), TransactionError> {
self.delete_item_by_key::<MediaMetadata, IndexedMediaMetadataUriKey>(source).await
}
pub async fn delete_media_metadata_by_content_size(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
content_size: impl Into<IndexedKeyRange<usize>>,
) -> Result<(), TransactionError> {
let range = Into::<IndexedKeyRange<usize>>::into(content_size)
.map(|size| (ignore_policy, size))
.into_prefix(self.serializer().inner());
self.delete_items_by_key::<MediaMetadata, IndexedMediaMetadataContentSizeKey>(range).await
}
pub async fn delete_media_metadata_by_content_size_greater_than(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
content_size: usize,
) -> Result<(), TransactionError> {
let (_, upper, _) =
IndexedMediaMetadataContentSizeKey::upper_key_components_with_prefix(ignore_policy);
self.delete_media_metadata_by_content_size(ignore_policy, (content_size + 1, upper)).await
}
pub async fn delete_media_metadata_by_last_access(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
last_access: impl Into<IndexedKeyRange<UnixTime>>,
) -> Result<(), TransactionError> {
let range = Into::<IndexedKeyRange<UnixTime>>::into(last_access)
.map(|last_access| (ignore_policy, last_access))
.into_prefix(self.serializer().inner());
self.delete_items_by_key::<MediaMetadata, IndexedMediaMetadataLastAccessKey>(range).await
}
pub async fn delete_media_metadata_by_last_access_earlier_than(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
time: UnixTime,
) -> Result<(), TransactionError> {
let (_, lower, _) =
IndexedMediaMetadataLastAccessKey::lower_key_components_with_prefix(ignore_policy);
self.delete_media_metadata_by_last_access(ignore_policy, (lower, time)).await
}
pub async fn delete_media_metadata_by_retention(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
range: impl Into<IndexedKeyRange<(UnixTime, usize)>>,
) -> Result<(), TransactionError> {
let range = Into::<IndexedKeyRange<(UnixTime, usize)>>::into(range)
.map(|(last_access, content_size)| (ignore_policy, last_access, content_size))
.into_prefix(self.serializer().inner());
self.delete_items_by_key::<MediaMetadata, IndexedMediaMetadataRetentionKey>(range).await
}
pub async fn delete_media_metadata_by_retention_to(
&self,
ignore_policy: IgnoreMediaRetentionPolicy,
last_access: UnixTime,
content_size: usize,
) -> Result<(), TransactionError> {
let (_, lower_last_access, lower_content_size, _) =
IndexedMediaMetadataRetentionKey::lower_key_components_with_prefix(ignore_policy);
let lower = (lower_last_access, lower_content_size);
self.delete_media_metadata_by_retention(ignore_policy, (lower, (last_access, content_size)))
.await
}
pub async fn get_media_content_by_id(
&self,
id: Uuid,
) -> Result<Option<MediaContent>, TransactionError> {
self.get_item_by_key_components::<MediaContent, IndexedMediaContentIdKey>(id).await
}
pub async fn add_media_content(
&self,
content: &MediaContent,
) -> Result<IndexedMediaContent, TransactionError> {
self.add_item(content).await
}
pub async fn put_media_content(
&self,
content: &MediaContent,
) -> Result<IndexedMediaContent, TransactionError> {
self.put_item(content).await
}
pub async fn put_media_content_if_policy_compliant(
&self,
media: &MediaContent,
policy: MediaRetentionPolicy,
) -> Result<Option<IndexedMediaContent>, TransactionError> {
self.put_item_if(media, |indexed| {
!policy.exceeds_max_file_size(indexed.content.len() as u64)
})
.await
}
pub async fn delete_media_content_by_id(&self, id: Uuid) -> Result<(), TransactionError> {
self.delete_item_by_key::<MediaContent, IndexedMediaContentIdKey>(id).await
}
}