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, CrossProcessLockError, CrossProcessLockGuard, TryLock,
36};
37use matrix_sdk_store_encryption::Error as StoreEncryptionError;
38pub use traits::{DynMediaStore, IntoMediaStore, MediaStore, MediaStoreInner};
39
40#[cfg(any(test, feature = "testing"))]
41pub use self::integration_tests::{MediaStoreInnerIntegrationTests, MediaStoreIntegrationTests};
42pub use self::{
43 media_retention_policy::MediaRetentionPolicy,
44 media_service::{IgnoreMediaRetentionPolicy, MediaService},
45 memory_store::MemoryMediaStore,
46};
47
48#[derive(Debug, thiserror::Error)]
50pub enum MediaStoreError {
51 #[error(transparent)]
53 Backend(Box<dyn std::error::Error + Send + Sync>),
54
55 #[error("Error encrypting or decrypting data from the media store: {0}")]
57 Encryption(#[from] StoreEncryptionError),
58
59 #[error("The store contains invalid data: {details}")]
61 InvalidData {
62 details: String,
64 },
65
66 #[error("Error serializing or deserializing data from the media store: {0}")]
68 Serialization(#[from] serde_json::Error),
69}
70
71impl MediaStoreError {
72 #[inline]
76 pub fn backend<E>(error: E) -> Self
77 where
78 E: std::error::Error + Send + Sync + 'static,
79 {
80 Self::Backend(Box::new(error))
81 }
82}
83
84pub type Result<T, E = MediaStoreError> = std::result::Result<T, E>;
86
87#[derive(Clone)]
89pub struct MediaStoreLock {
90 cross_process_lock: Arc<CrossProcessLock<LockableMediaStore>>,
92
93 store: Arc<DynMediaStore>,
97}
98
99#[cfg(not(tarpaulin_include))]
100impl fmt::Debug for MediaStoreLock {
101 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
102 formatter.debug_struct("MediaStoreLock").finish_non_exhaustive()
103 }
104}
105
106impl MediaStoreLock {
107 pub fn new<S>(store: S, holder: String) -> Self
112 where
113 S: IntoMediaStore,
114 {
115 let store = store.into_media_store();
116
117 Self {
118 cross_process_lock: Arc::new(CrossProcessLock::new(
119 LockableMediaStore(store.clone()),
120 "default".to_owned(),
121 holder,
122 )),
123 store,
124 }
125 }
126
127 pub async fn lock(&self) -> Result<MediaStoreLockGuard<'_>, CrossProcessLockError> {
129 let cross_process_lock_guard = self.cross_process_lock.spin_lock(None).await?;
130
131 Ok(MediaStoreLockGuard { cross_process_lock_guard, store: self.store.deref() })
132 }
133}
134
135pub struct MediaStoreLockGuard<'a> {
139 #[allow(unused)]
141 cross_process_lock_guard: CrossProcessLockGuard,
142
143 store: &'a DynMediaStore,
145}
146
147#[cfg(not(tarpaulin_include))]
148impl fmt::Debug for MediaStoreLockGuard<'_> {
149 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
150 formatter.debug_struct("MediaStoreLockGuard").finish_non_exhaustive()
151 }
152}
153
154impl Deref for MediaStoreLockGuard<'_> {
155 type Target = DynMediaStore;
156
157 fn deref(&self) -> &Self::Target {
158 self.store
159 }
160}
161
162#[derive(Clone, Debug)]
165struct LockableMediaStore(Arc<DynMediaStore>);
166
167impl TryLock for LockableMediaStore {
168 type LockError = MediaStoreError;
169
170 async fn try_lock(
171 &self,
172 lease_duration_ms: u32,
173 key: &str,
174 holder: &str,
175 ) -> std::result::Result<bool, Self::LockError> {
176 self.0.try_take_leased_lock(lease_duration_ms, key, holder).await
177 }
178}