1#![cfg_attr(
15 not(any(feature = "state-store", feature = "crypto-store", feature = "event-cache")),
16 allow(dead_code, unused_imports)
17)]
18
19#[cfg(feature = "crypto-store")]
20mod crypto_store;
21mod error;
22#[cfg(feature = "event-cache")]
23mod event_cache_store;
24#[cfg(feature = "event-cache")]
25mod media_store;
26#[cfg(feature = "state-store")]
27mod state_store;
28mod utils;
29use std::{
30 cmp::max,
31 fmt,
32 path::{Path, PathBuf},
33};
34
35use deadpool_sqlite::PoolConfig;
36use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
37
38#[cfg(feature = "crypto-store")]
39pub use self::crypto_store::SqliteCryptoStore;
40pub use self::error::OpenStoreError;
41#[cfg(feature = "event-cache")]
42pub use self::event_cache_store::SqliteEventCacheStore;
43#[cfg(feature = "event-cache")]
44pub use self::media_store::SqliteMediaStore;
45#[cfg(feature = "state-store")]
46pub use self::state_store::{SqliteStateStore, DATABASE_NAME as STATE_STORE_DATABASE_NAME};
47
48#[cfg(test)]
49matrix_sdk_test_utils::init_tracing_for_tests!();
50
51#[derive(Clone, Debug, PartialEq, Zeroize, ZeroizeOnDrop)]
53pub enum Secret {
54 Key(Box<[u8; 32]>),
56 PassPhrase(Zeroizing<String>),
58}
59
60#[derive(Clone)]
62pub struct SqliteStoreConfig {
63 path: PathBuf,
65 secret: Option<Secret>,
67 pool_config: PoolConfig,
69 runtime_config: RuntimeConfig,
71}
72
73impl fmt::Debug for SqliteStoreConfig {
74 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
75 formatter
76 .debug_struct("SqliteStoreConfig")
77 .field("path", &self.path)
78 .field("pool_config", &self.pool_config)
79 .field("runtime_config", &self.runtime_config)
80 .finish_non_exhaustive()
81 }
82}
83
84const POOL_MINIMUM_SIZE: usize = 2;
89
90impl SqliteStoreConfig {
91 pub fn new<P>(path: P) -> Self
94 where
95 P: AsRef<Path>,
96 {
97 Self {
98 path: path.as_ref().to_path_buf(),
99 pool_config: PoolConfig::new(max(POOL_MINIMUM_SIZE, num_cpus::get_physical() * 4)),
100 runtime_config: RuntimeConfig::default(),
101 secret: None,
102 }
103 }
104
105 pub fn with_low_memory_config<P>(path: P) -> Self
115 where
116 P: AsRef<Path>,
117 {
118 Self::new(path)
119 .pool_max_size(num_cpus::get_physical())
121 .cache_size(500_000)
123 .journal_size_limit(2_000_000)
125 }
126
127 pub fn path<P>(mut self, path: P) -> Self
129 where
130 P: AsRef<Path>,
131 {
132 self.path = path.as_ref().to_path_buf();
133 self
134 }
135
136 pub fn passphrase(mut self, passphrase: Option<&str>) -> Self {
138 self.secret =
139 passphrase.map(|passphrase| Secret::PassPhrase(Zeroizing::new(passphrase.to_owned())));
140 self
141 }
142
143 pub fn key(mut self, key: Option<&[u8; 32]>) -> Self {
145 self.secret = key.map(|key| Secret::Key(Box::new(*key)));
146 self
147 }
148
149 pub fn pool_max_size(mut self, max_size: usize) -> Self {
153 self.pool_config.max_size = max(POOL_MINIMUM_SIZE, max_size);
154 self
155 }
156
157 pub fn optimize(mut self, optimize: bool) -> Self {
169 self.runtime_config.optimize = optimize;
170 self
171 }
172
173 pub fn cache_size(mut self, cache_size: u32) -> Self {
181 self.runtime_config.cache_size = cache_size;
182 self
183 }
184
185 pub fn journal_size_limit(mut self, limit: u32) -> Self {
205 self.runtime_config.journal_size_limit = limit;
206 self
207 }
208}
209
210#[derive(Clone, Debug)]
215struct RuntimeConfig {
216 optimize: bool,
218
219 cache_size: u32,
222
223 journal_size_limit: u32,
227}
228
229impl Default for RuntimeConfig {
230 fn default() -> Self {
231 Self {
232 optimize: true,
234 cache_size: 2_000_000,
236 journal_size_limit: 10_000_000,
238 }
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use std::{
245 ops::Not,
246 path::{Path, PathBuf},
247 };
248
249 use super::{Secret, SqliteStoreConfig, POOL_MINIMUM_SIZE};
250
251 #[test]
252 fn test_new() {
253 let store_config = SqliteStoreConfig::new(Path::new("foo"));
254
255 assert_eq!(store_config.pool_config.max_size, num_cpus::get_physical() * 4);
256 assert!(store_config.runtime_config.optimize);
257 assert_eq!(store_config.runtime_config.cache_size, 2_000_000);
258 assert_eq!(store_config.runtime_config.journal_size_limit, 10_000_000);
259 }
260
261 #[test]
262 fn test_with_low_memory_config() {
263 let store_config = SqliteStoreConfig::with_low_memory_config(Path::new("foo"));
264
265 assert_eq!(store_config.pool_config.max_size, num_cpus::get_physical());
266 assert!(store_config.runtime_config.optimize);
267 assert_eq!(store_config.runtime_config.cache_size, 500_000);
268 assert_eq!(store_config.runtime_config.journal_size_limit, 2_000_000);
269 }
270
271 #[test]
272 fn test_store_config_when_passphrase() {
273 let store_config = SqliteStoreConfig::new(Path::new("foo"))
274 .passphrase(Some("bar"))
275 .pool_max_size(42)
276 .optimize(false)
277 .cache_size(43)
278 .journal_size_limit(44);
279
280 assert_eq!(store_config.path, PathBuf::from("foo"));
281 assert_eq!(store_config.secret, Some(Secret::PassPhrase("bar".to_owned().into())));
282 assert_eq!(store_config.pool_config.max_size, 42);
283 assert!(store_config.runtime_config.optimize.not());
284 assert_eq!(store_config.runtime_config.cache_size, 43);
285 assert_eq!(store_config.runtime_config.journal_size_limit, 44);
286 }
287
288 #[test]
289 fn test_store_config_when_key() {
290 let store_config = SqliteStoreConfig::new(Path::new("foo"))
291 .key(Some(&[
292 143, 27, 202, 78, 96, 55, 13, 149, 247, 8, 33, 120, 204, 92, 171, 66, 19, 238, 61,
293 107, 132, 211, 40, 244, 71, 190, 99, 14, 173, 225, 6, 156,
294 ]))
295 .pool_max_size(42)
296 .optimize(false)
297 .cache_size(43)
298 .journal_size_limit(44);
299
300 assert_eq!(store_config.path, PathBuf::from("foo"));
301 assert_eq!(
302 store_config.secret,
303 Some(Secret::Key(Box::new([
304 143, 27, 202, 78, 96, 55, 13, 149, 247, 8, 33, 120, 204, 92, 171, 66, 19, 238, 61,
305 107, 132, 211, 40, 244, 71, 190, 99, 14, 173, 225, 6, 156,
306 ])))
307 );
308 assert_eq!(store_config.pool_config.max_size, 42);
309 assert!(store_config.runtime_config.optimize.not());
310 assert_eq!(store_config.runtime_config.cache_size, 43);
311 assert_eq!(store_config.runtime_config.journal_size_limit, 44);
312 }
313
314 #[test]
315 fn test_store_config_path() {
316 let store_config = SqliteStoreConfig::new(Path::new("foo")).path(Path::new("bar"));
317
318 assert_eq!(store_config.path, PathBuf::from("bar"));
319 }
320
321 #[test]
322 fn test_pool_size_has_a_minimum() {
323 let store_config = SqliteStoreConfig::new(Path::new("foo")).pool_max_size(1);
324
325 assert_eq!(store_config.pool_config.max_size, POOL_MINIMUM_SIZE);
326 }
327}