use std::ops::Deref;
use indexed_db_futures::transaction as inner;
use matrix_sdk_base::{
event_cache::{Event as RawEvent, Gap as RawGap},
linked_chunk::{ChunkContent, ChunkIdentifier, LinkedChunkId, RawChunk},
};
use ruma::{EventId, RoomId, events::relation::RelationType};
use serde::{Serialize, de::DeserializeOwned};
use crate::{
error::AsyncErrorDeps,
event_cache_store::{
serializer::indexed_types::{
IndexedChunk, IndexedChunkIdKey, IndexedEvent, IndexedEventIdKey,
IndexedEventPositionKey, IndexedEventRelationKey, IndexedEventRoomKey, IndexedGapIdKey,
IndexedLease, IndexedLeaseIdKey, IndexedNextChunkIdKey,
},
types::{Chunk, ChunkType, Event, Gap, Lease, Position},
},
serializer::indexed_type::{
IndexedTypeSerializer,
range::IndexedKeyRange,
traits::{Indexed, IndexedPrefixKeyBounds, IndexedPrefixKeyComponentBounds},
},
transaction::{Transaction, TransactionError},
};
pub struct IndexeddbEventCacheStoreTransaction<'a> {
transaction: Transaction<'a>,
}
impl<'a> Deref for IndexeddbEventCacheStoreTransaction<'a> {
type Target = Transaction<'a>;
fn deref(&self) -> &Self::Target {
&self.transaction
}
}
impl<'a> IndexeddbEventCacheStoreTransaction<'a> {
pub fn new(transaction: inner::Transaction<'a>, serializer: &'a IndexedTypeSerializer) -> Self {
Self { transaction: Transaction::new(transaction, serializer) }
}
pub async fn commit(self) -> Result<(), TransactionError> {
self.transaction.commit().await
}
pub async fn get_items_by_linked_chunk_id<'b, T, K>(
&self,
linked_chunk_id: LinkedChunkId<'b>,
) -> Result<Vec<T>, TransactionError>
where
T: Indexed,
T::IndexedType: DeserializeOwned,
T::Error: AsyncErrorDeps,
K: IndexedPrefixKeyBounds<T, LinkedChunkId<'b>> + Serialize,
{
self.get_items_by_key::<T, K>(IndexedKeyRange::all_with_prefix(
linked_chunk_id,
self.serializer().inner(),
))
.await
}
pub async fn get_items_in_room<'b, T, K>(
&self,
room_id: &'b RoomId,
) -> Result<Vec<T>, TransactionError>
where
T: Indexed,
T::IndexedType: DeserializeOwned,
T::Error: AsyncErrorDeps,
K: IndexedPrefixKeyBounds<T, &'b RoomId> + Serialize,
{
self.get_items_by_key::<T, K>(IndexedKeyRange::all_with_prefix(
room_id,
self.serializer().inner(),
))
.await
}
pub async fn get_items_count_by_linked_chunk_id<'b, T, K>(
&self,
linked_chunk_id: LinkedChunkId<'b>,
) -> Result<usize, TransactionError>
where
T: Indexed,
T::IndexedType: DeserializeOwned,
T::Error: AsyncErrorDeps,
K: IndexedPrefixKeyBounds<T, LinkedChunkId<'b>> + Serialize,
{
self.get_items_count_by_key::<T, K>(IndexedKeyRange::all_with_prefix(
linked_chunk_id,
self.serializer().inner(),
))
.await
}
pub async fn delete_items_by_linked_chunk_id<'b, T, K>(
&self,
linked_chunk_id: LinkedChunkId<'b>,
) -> Result<(), TransactionError>
where
T: Indexed,
K: IndexedPrefixKeyBounds<T, LinkedChunkId<'b>> + Serialize,
{
self.delete_items_by_key::<T, K>(IndexedKeyRange::all_with_prefix(
linked_chunk_id,
self.serializer().inner(),
))
.await
}
pub async fn get_lease_by_id(&self, id: &str) -> Result<Option<Lease>, TransactionError> {
self.get_item_by_key_components::<Lease, IndexedLeaseIdKey>(id).await
}
pub async fn put_lease(&self, lease: &Lease) -> Result<IndexedLease, TransactionError> {
self.put_item(lease).await
}
pub async fn get_chunk_by_id(
&self,
linked_chunk_id: LinkedChunkId<'_>,
chunk_id: ChunkIdentifier,
) -> Result<Option<Chunk>, TransactionError> {
self.get_item_by_key_components::<Chunk, IndexedChunkIdKey>((linked_chunk_id, chunk_id))
.await
}
pub async fn get_chunk_by_next_chunk_id(
&self,
linked_chunk_id: LinkedChunkId<'_>,
next_chunk_id: Option<ChunkIdentifier>,
) -> Result<Option<Chunk>, TransactionError> {
self.get_item_by_key_components::<Chunk, IndexedNextChunkIdKey>((
linked_chunk_id,
next_chunk_id,
))
.await
}
pub async fn get_chunks_by_linked_chunk_id(
&self,
linked_chunk_id: LinkedChunkId<'_>,
) -> Result<Vec<Chunk>, TransactionError> {
self.get_items_by_linked_chunk_id::<Chunk, IndexedChunkIdKey>(linked_chunk_id).await
}
pub async fn get_chunks_count_by_linked_chunk_id(
&self,
linked_chunk_id: LinkedChunkId<'_>,
) -> Result<usize, TransactionError> {
self.get_items_count_by_linked_chunk_id::<Chunk, IndexedChunkIdKey>(linked_chunk_id).await
}
pub async fn get_max_chunk_by_id(
&self,
linked_chunk_id: LinkedChunkId<'_>,
) -> Result<Option<Chunk>, TransactionError> {
let range = IndexedKeyRange::all_with_prefix::<Chunk, _>(
linked_chunk_id,
self.serializer().inner(),
);
self.get_max_item_by_key::<Chunk, IndexedChunkIdKey>(range).await
}
pub async fn load_chunk_by_id(
&self,
linked_chunk_id: LinkedChunkId<'_>,
chunk_id: ChunkIdentifier,
) -> Result<Option<RawChunk<RawEvent, RawGap>>, TransactionError> {
if let Some(chunk) = self.get_chunk_by_id(linked_chunk_id, chunk_id).await? {
let content = match chunk.chunk_type {
ChunkType::Event => {
let events = self
.get_events_by_chunk(
linked_chunk_id,
ChunkIdentifier::new(chunk.identifier),
)
.await?
.into_iter()
.map(RawEvent::from)
.collect();
ChunkContent::Items(events)
}
ChunkType::Gap => {
let gap = self
.get_gap_by_id(linked_chunk_id, ChunkIdentifier::new(chunk.identifier))
.await?
.ok_or(TransactionError::ItemNotFound)?;
ChunkContent::Gap(RawGap { token: gap.token })
}
};
return Ok(Some(RawChunk {
identifier: ChunkIdentifier::new(chunk.identifier),
content,
previous: chunk.previous.map(ChunkIdentifier::new),
next: chunk.next.map(ChunkIdentifier::new),
}));
}
Ok(None)
}
pub async fn add_chunk(&self, chunk: &Chunk) -> Result<IndexedChunk, TransactionError> {
let indexed = self.add_item(chunk).await?;
if let Some(previous) = chunk.previous {
let previous_identifier = ChunkIdentifier::new(previous);
let mut previous_chunk = self
.get_chunk_by_id(chunk.linked_chunk_id.as_ref(), previous_identifier)
.await?
.ok_or(TransactionError::ItemNotFound)?;
previous_chunk.next = Some(chunk.identifier);
self.put_item(&previous_chunk).await?;
}
if let Some(next) = chunk.next {
let next_identifier = ChunkIdentifier::new(next);
let mut next_chunk = self
.get_chunk_by_id(chunk.linked_chunk_id.as_ref(), next_identifier)
.await?
.ok_or(TransactionError::ItemNotFound)?;
next_chunk.previous = Some(chunk.identifier);
self.put_item(&next_chunk).await?;
}
Ok(indexed)
}
pub async fn delete_chunk_by_id(
&self,
linked_chunk_id: LinkedChunkId<'_>,
chunk_id: ChunkIdentifier,
) -> Result<(), TransactionError> {
if let Some(chunk) = self.get_chunk_by_id(linked_chunk_id, chunk_id).await? {
if let Some(previous) = chunk.previous {
let previous_identifier = ChunkIdentifier::new(previous);
if let Some(mut previous_chunk) =
self.get_chunk_by_id(linked_chunk_id, previous_identifier).await?
{
previous_chunk.next = chunk.next;
self.put_item(&previous_chunk).await?;
}
}
if let Some(next) = chunk.next {
let next_identifier = ChunkIdentifier::new(next);
if let Some(mut next_chunk) =
self.get_chunk_by_id(linked_chunk_id, next_identifier).await?
{
next_chunk.previous = chunk.previous;
self.put_item(&next_chunk).await?;
}
}
self.delete_item_by_key::<Chunk, IndexedChunkIdKey>((linked_chunk_id, chunk_id))
.await?;
match chunk.chunk_type {
ChunkType::Event => {
self.delete_events_by_chunk(linked_chunk_id, chunk_id).await?;
}
ChunkType::Gap => {
self.delete_gap_by_id(linked_chunk_id, chunk_id).await?;
}
}
}
Ok(())
}
pub async fn delete_chunks_by_linked_chunk_id(
&self,
linked_chunk_id: LinkedChunkId<'_>,
) -> Result<(), TransactionError> {
self.delete_items_by_linked_chunk_id::<Chunk, IndexedChunkIdKey>(linked_chunk_id).await
}
pub async fn get_event_by_id(
&self,
linked_chunk_id: LinkedChunkId<'_>,
event_id: &EventId,
) -> Result<Option<Event>, TransactionError> {
let key = self.serializer().encode_key((linked_chunk_id, event_id));
self.get_item_by_key::<Event, IndexedEventIdKey>(key).await
}
pub async fn get_event_by_room(
&self,
room_id: &RoomId,
event_id: &EventId,
) -> Result<Option<Event>, TransactionError> {
let key = self.serializer().encode_key((room_id, event_id));
self.get_item_by_key::<Event, IndexedEventRoomKey>(key).await
}
pub async fn get_events_by_room(
&self,
room_id: &RoomId,
event_id: &EventId,
) -> Result<Vec<Event>, TransactionError> {
let key: IndexedEventRoomKey = self.serializer().encode_key((room_id, event_id));
self.get_items_by_key::<Event, IndexedEventRoomKey>(key).await
}
pub async fn get_room_events(&self, room_id: &RoomId) -> Result<Vec<Event>, TransactionError> {
self.get_items_in_room::<Event, IndexedEventRoomKey>(room_id).await
}
pub async fn get_events_by_chunk(
&self,
linked_chunk_id: LinkedChunkId<'_>,
chunk_id: ChunkIdentifier,
) -> Result<Vec<Event>, TransactionError> {
let range = IndexedKeyRange::all_with_prefix(
(linked_chunk_id, chunk_id),
self.serializer().inner(),
);
self.get_items_by_key::<Event, IndexedEventPositionKey>(range).await
}
pub async fn get_events_count_by_chunk(
&self,
linked_chunk_id: LinkedChunkId<'_>,
chunk_id: ChunkIdentifier,
) -> Result<usize, TransactionError> {
let range = IndexedKeyRange::all_with_prefix(
(linked_chunk_id, chunk_id),
self.serializer().inner(),
);
self.get_items_count_by_key::<Event, IndexedEventPositionKey>(range).await
}
pub async fn get_events_by_relation(
&self,
room_id: &RoomId,
range: impl Into<IndexedKeyRange<(&EventId, &RelationType)>>,
) -> Result<Vec<Event>, TransactionError> {
let range = range
.into()
.map(|(event_id, relation_type)| (room_id, event_id, relation_type))
.encoded(self.serializer().inner());
self.get_items_by_key::<Event, IndexedEventRelationKey>(range).await
}
pub async fn get_events_by_related_event(
&self,
room_id: &RoomId,
related_event_id: &EventId,
) -> Result<Vec<Event>, TransactionError> {
let range = IndexedKeyRange::all_with_prefix(
(room_id, related_event_id),
self.serializer().inner(),
);
self.get_items_by_key::<Event, IndexedEventRelationKey>(range).await
}
pub async fn put_event(&self, event: &Event) -> Result<IndexedEvent, TransactionError> {
if let Some(position) = event.position() {
self.delete_event_by_position(event.linked_chunk_id(), position).await?;
}
self.put_item(event).await
}
pub async fn delete_events_by_position(
&self,
linked_chunk_id: LinkedChunkId<'_>,
range: impl Into<IndexedKeyRange<Position>>,
) -> Result<(), TransactionError> {
self.delete_items_by_key_components::<Event, IndexedEventPositionKey>(
range.into().map(|position| (linked_chunk_id, position)),
)
.await
}
pub async fn delete_event_by_position(
&self,
linked_chunk_id: LinkedChunkId<'_>,
position: Position,
) -> Result<(), TransactionError> {
self.delete_item_by_key::<Event, IndexedEventPositionKey>((linked_chunk_id, position)).await
}
pub async fn delete_events_by_chunk(
&self,
linked_chunk_id: LinkedChunkId<'_>,
chunk_id: ChunkIdentifier,
) -> Result<(), TransactionError> {
let range = IndexedKeyRange::all_with_prefix(
(linked_chunk_id, chunk_id),
self.serializer().inner(),
);
self.delete_items_by_key::<Event, IndexedEventPositionKey>(range).await
}
pub async fn delete_events_by_chunk_from_index(
&self,
linked_chunk_id: LinkedChunkId<'_>,
position: Position,
) -> Result<(), TransactionError> {
let lower = (linked_chunk_id, position);
let upper = IndexedEventPositionKey::upper_key_components_with_prefix((
linked_chunk_id,
ChunkIdentifier::new(position.chunk_identifier),
));
let range = IndexedKeyRange::Bound(lower, upper).map(|(_, position)| position);
self.delete_events_by_position(linked_chunk_id, range).await
}
pub async fn delete_events_by_linked_chunk_id(
&self,
linked_chunk_id: LinkedChunkId<'_>,
) -> Result<(), TransactionError> {
self.delete_items_by_linked_chunk_id::<Event, IndexedEventIdKey>(linked_chunk_id).await
}
pub async fn get_gap_by_id(
&self,
linked_chunk_id: LinkedChunkId<'_>,
chunk_id: ChunkIdentifier,
) -> Result<Option<Gap>, TransactionError> {
self.get_item_by_key_components::<Gap, IndexedGapIdKey>((linked_chunk_id, chunk_id)).await
}
pub async fn delete_gap_by_id(
&self,
linked_chunk_id: LinkedChunkId<'_>,
chunk_id: ChunkIdentifier,
) -> Result<(), TransactionError> {
self.delete_item_by_key::<Gap, IndexedGapIdKey>((linked_chunk_id, chunk_id)).await
}
pub async fn delete_gaps_by_linked_chunk_id(
&self,
linked_chunk_id: LinkedChunkId<'_>,
) -> Result<(), TransactionError> {
self.delete_items_by_linked_chunk_id::<Gap, IndexedGapIdKey>(linked_chunk_id).await
}
}