matrix_sdk_base/media/store/
mod.rs1mod media_retention_policy;
23mod media_service;
24mod memory_store;
25mod traits;
26#[cfg(any(test, feature = "testing"))]
27#[macro_use]
28pub mod integration_tests;
29
30#[cfg(not(tarpaulin_include))]
31use std::fmt;
32use std::{ops::Deref, sync::Arc};
33
34use matrix_sdk_common::cross_process_lock::{
35 CrossProcessLock, CrossProcessLockConfig, CrossProcessLockError, CrossProcessLockGeneration,
36 CrossProcessLockGuard, CrossProcessLockState, TryLock,
37};
38use matrix_sdk_store_encryption::Error as StoreEncryptionError;
39pub use traits::{DynMediaStore, IntoMediaStore, MediaStore, MediaStoreInner};
40
41#[cfg(any(test, feature = "testing"))]
42pub use self::integration_tests::{MediaStoreInnerIntegrationTests, MediaStoreIntegrationTests};
43pub use self::{
44 media_retention_policy::MediaRetentionPolicy,
45 media_service::{IgnoreMediaRetentionPolicy, MediaService},
46 memory_store::MemoryMediaStore,
47};
48
49#[derive(Debug, thiserror::Error)]
51pub enum MediaStoreError {
52 #[error(transparent)]
54 Backend(Box<dyn std::error::Error + Send + Sync>),
55
56 #[error("Error encrypting or decrypting data from the media store: {0}")]
58 Encryption(#[from] StoreEncryptionError),
59
60 #[error("The store contains invalid data: {details}")]
62 InvalidData {
63 details: String,
65 },
66
67 #[error("Error serializing or deserializing data from the media store: {0}")]
69 Serialization(#[from] serde_json::Error),
70}
71
72impl MediaStoreError {
73 #[inline]
77 pub fn backend<E>(error: E) -> Self
78 where
79 E: std::error::Error + Send + Sync + 'static,
80 {
81 Self::Backend(Box::new(error))
82 }
83}
84
85impl From<MediaStoreError> for CrossProcessLockError {
86 fn from(value: MediaStoreError) -> Self {
87 Self::TryLock(Box::new(value))
88 }
89}
90
91pub type Result<T, E = MediaStoreError> = std::result::Result<T, E>;
93
94#[derive(Clone)]
96pub struct MediaStoreLock {
97 cross_process_lock: Arc<CrossProcessLock<LockableMediaStore>>,
99
100 store: Arc<DynMediaStore>,
104}
105
106#[cfg(not(tarpaulin_include))]
107impl fmt::Debug for MediaStoreLock {
108 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
109 formatter.debug_struct("MediaStoreLock").finish_non_exhaustive()
110 }
111}
112
113impl MediaStoreLock {
114 pub fn new<S>(store: S, cross_process_lock_config: CrossProcessLockConfig) -> Self
119 where
120 S: IntoMediaStore,
121 {
122 let store = store.into_media_store();
123
124 let cross_process_lock = Arc::new(CrossProcessLock::new(
125 LockableMediaStore(store.clone()),
126 "default".to_owned(),
127 cross_process_lock_config,
128 ));
129 Self { cross_process_lock, store }
130 }
131
132 pub async fn lock(&self) -> Result<MediaStoreLockGuard<'_>, CrossProcessLockError> {
134 let cross_process_lock_guard = match self.cross_process_lock.spin_lock(None).await?? {
135 CrossProcessLockState::Clean(guard) => guard,
137
138 CrossProcessLockState::Dirty(guard) => {
144 guard.clear_dirty();
145
146 guard
147 }
148 };
149
150 Ok(MediaStoreLockGuard { cross_process_lock_guard, store: self.store.deref() })
151 }
152}
153
154pub struct MediaStoreLockGuard<'a> {
158 #[allow(unused)]
160 cross_process_lock_guard: CrossProcessLockGuard,
161
162 store: &'a DynMediaStore,
164}
165
166#[cfg(not(tarpaulin_include))]
167impl fmt::Debug for MediaStoreLockGuard<'_> {
168 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
169 formatter.debug_struct("MediaStoreLockGuard").finish_non_exhaustive()
170 }
171}
172
173impl Deref for MediaStoreLockGuard<'_> {
174 type Target = DynMediaStore;
175
176 fn deref(&self) -> &Self::Target {
177 self.store
178 }
179}
180
181#[derive(Clone, Debug)]
184struct LockableMediaStore(Arc<DynMediaStore>);
185
186impl TryLock for LockableMediaStore {
187 type LockError = MediaStoreError;
188
189 async fn try_lock(
190 &self,
191 lease_duration_ms: u32,
192 key: &str,
193 holder: &str,
194 ) -> std::result::Result<Option<CrossProcessLockGeneration>, Self::LockError> {
195 self.0.try_take_leased_lock(lease_duration_ms, key, holder).await
196 }
197}