use std::{fmt, sync::Arc};
use async_trait::async_trait;
use matrix_sdk_common::{
AsyncTraitDeps,
cross_process_lock::CrossProcessLockGeneration,
linked_chunk::{
ChunkIdentifier, ChunkIdentifierGenerator, ChunkMetadata, LinkedChunkId, Position,
RawChunk, Update,
},
};
use ruma::{EventId, OwnedEventId, RoomId, events::relation::RelationType};
use super::EventCacheStoreError;
use crate::event_cache::{Event, Gap};
pub const DEFAULT_CHUNK_CAPACITY: usize = 128;
#[cfg_attr(target_family = "wasm", async_trait(?Send))]
#[cfg_attr(not(target_family = "wasm"), async_trait)]
pub trait EventCacheStore: AsyncTraitDeps {
type Error: fmt::Debug + Into<EventCacheStoreError>;
async fn try_take_leased_lock(
&self,
lease_duration_ms: u32,
key: &str,
holder: &str,
) -> Result<Option<CrossProcessLockGeneration>, Self::Error>;
async fn handle_linked_chunk_updates(
&self,
linked_chunk_id: LinkedChunkId<'_>,
updates: Vec<Update<Event, Gap>>,
) -> Result<(), Self::Error>;
async fn remove_room(&self, room_id: &RoomId) -> Result<(), Self::Error> {
self.handle_linked_chunk_updates(LinkedChunkId::Room(room_id), vec![Update::Clear]).await
}
#[doc(hidden)]
async fn load_all_chunks(
&self,
linked_chunk_id: LinkedChunkId<'_>,
) -> Result<Vec<RawChunk<Event, Gap>>, Self::Error>;
async fn load_all_chunks_metadata(
&self,
linked_chunk_id: LinkedChunkId<'_>,
) -> Result<Vec<ChunkMetadata>, Self::Error>;
async fn load_last_chunk(
&self,
linked_chunk_id: LinkedChunkId<'_>,
) -> Result<(Option<RawChunk<Event, Gap>>, ChunkIdentifierGenerator), Self::Error>;
async fn load_previous_chunk(
&self,
linked_chunk_id: LinkedChunkId<'_>,
before_chunk_identifier: ChunkIdentifier,
) -> Result<Option<RawChunk<Event, Gap>>, Self::Error>;
async fn clear_all_linked_chunks(&self) -> Result<(), Self::Error>;
async fn filter_duplicated_events(
&self,
linked_chunk_id: LinkedChunkId<'_>,
events: Vec<OwnedEventId>,
) -> Result<Vec<(OwnedEventId, Position)>, Self::Error>;
async fn find_event(
&self,
room_id: &RoomId,
event_id: &EventId,
) -> Result<Option<Event>, Self::Error>;
async fn find_event_relations(
&self,
room_id: &RoomId,
event_id: &EventId,
filter: Option<&[RelationType]>,
) -> Result<Vec<(Event, Option<Position>)>, Self::Error>;
async fn get_room_events(
&self,
room_id: &RoomId,
event_type: Option<&str>,
session_id: Option<&str>,
) -> Result<Vec<Event>, Self::Error>;
async fn save_event(&self, room_id: &RoomId, event: Event) -> Result<(), Self::Error>;
#[doc(hidden)]
async fn optimize(&self) -> Result<(), Self::Error>;
async fn get_size(&self) -> Result<Option<usize>, Self::Error>;
}
#[repr(transparent)]
struct EraseEventCacheStoreError<T>(T);
#[cfg(not(tarpaulin_include))]
impl<T: fmt::Debug> fmt::Debug for EraseEventCacheStoreError<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg_attr(target_family = "wasm", async_trait(?Send))]
#[cfg_attr(not(target_family = "wasm"), async_trait)]
impl<T: EventCacheStore> EventCacheStore for EraseEventCacheStoreError<T> {
type Error = EventCacheStoreError;
async fn try_take_leased_lock(
&self,
lease_duration_ms: u32,
key: &str,
holder: &str,
) -> Result<Option<CrossProcessLockGeneration>, Self::Error> {
self.0.try_take_leased_lock(lease_duration_ms, key, holder).await.map_err(Into::into)
}
async fn handle_linked_chunk_updates(
&self,
linked_chunk_id: LinkedChunkId<'_>,
updates: Vec<Update<Event, Gap>>,
) -> Result<(), Self::Error> {
self.0.handle_linked_chunk_updates(linked_chunk_id, updates).await.map_err(Into::into)
}
async fn load_all_chunks(
&self,
linked_chunk_id: LinkedChunkId<'_>,
) -> Result<Vec<RawChunk<Event, Gap>>, Self::Error> {
self.0.load_all_chunks(linked_chunk_id).await.map_err(Into::into)
}
async fn load_all_chunks_metadata(
&self,
linked_chunk_id: LinkedChunkId<'_>,
) -> Result<Vec<ChunkMetadata>, Self::Error> {
self.0.load_all_chunks_metadata(linked_chunk_id).await.map_err(Into::into)
}
async fn load_last_chunk(
&self,
linked_chunk_id: LinkedChunkId<'_>,
) -> Result<(Option<RawChunk<Event, Gap>>, ChunkIdentifierGenerator), Self::Error> {
self.0.load_last_chunk(linked_chunk_id).await.map_err(Into::into)
}
async fn load_previous_chunk(
&self,
linked_chunk_id: LinkedChunkId<'_>,
before_chunk_identifier: ChunkIdentifier,
) -> Result<Option<RawChunk<Event, Gap>>, Self::Error> {
self.0
.load_previous_chunk(linked_chunk_id, before_chunk_identifier)
.await
.map_err(Into::into)
}
async fn clear_all_linked_chunks(&self) -> Result<(), Self::Error> {
self.0.clear_all_linked_chunks().await.map_err(Into::into)
}
async fn filter_duplicated_events(
&self,
linked_chunk_id: LinkedChunkId<'_>,
events: Vec<OwnedEventId>,
) -> Result<Vec<(OwnedEventId, Position)>, Self::Error> {
self.0.filter_duplicated_events(linked_chunk_id, events).await.map_err(Into::into)
}
async fn find_event(
&self,
room_id: &RoomId,
event_id: &EventId,
) -> Result<Option<Event>, Self::Error> {
self.0.find_event(room_id, event_id).await.map_err(Into::into)
}
async fn find_event_relations(
&self,
room_id: &RoomId,
event_id: &EventId,
filter: Option<&[RelationType]>,
) -> Result<Vec<(Event, Option<Position>)>, Self::Error> {
self.0.find_event_relations(room_id, event_id, filter).await.map_err(Into::into)
}
async fn get_room_events(
&self,
room_id: &RoomId,
event_type: Option<&str>,
session_id: Option<&str>,
) -> Result<Vec<Event>, Self::Error> {
self.0.get_room_events(room_id, event_type, session_id).await.map_err(Into::into)
}
async fn save_event(&self, room_id: &RoomId, event: Event) -> Result<(), Self::Error> {
self.0.save_event(room_id, event).await.map_err(Into::into)
}
async fn optimize(&self) -> Result<(), Self::Error> {
self.0.optimize().await.map_err(Into::into)?;
Ok(())
}
async fn get_size(&self) -> Result<Option<usize>, Self::Error> {
Ok(self.0.get_size().await.map_err(Into::into)?)
}
}
pub type DynEventCacheStore = dyn EventCacheStore<Error = EventCacheStoreError>;
pub trait IntoEventCacheStore {
#[doc(hidden)]
fn into_event_cache_store(self) -> Arc<DynEventCacheStore>;
}
impl IntoEventCacheStore for Arc<DynEventCacheStore> {
fn into_event_cache_store(self) -> Arc<DynEventCacheStore> {
self
}
}
impl<T> IntoEventCacheStore for T
where
T: EventCacheStore + Sized + 'static,
{
fn into_event_cache_store(self) -> Arc<DynEventCacheStore> {
Arc::new(EraseEventCacheStoreError(self))
}
}
impl<T> IntoEventCacheStore for Arc<T>
where
T: EventCacheStore + 'static,
{
fn into_event_cache_store(self) -> Arc<DynEventCacheStore> {
let ptr: *const T = Arc::into_raw(self);
let ptr_erased = ptr as *const EraseEventCacheStoreError<T>;
unsafe { Arc::from_raw(ptr_erased) }
}
}