1use std::path::{Path, PathBuf};
5
6use uuid::Uuid;
7
8#[derive(Debug, Clone, Eq, PartialEq)]
9pub enum DbPath {
10 File(PathBuf),
11 Tmpfs(PathBuf), Memory(PathBuf), }
14
15#[derive(Debug, Clone)]
17pub struct SqliteConfig {
18 pub path: DbPath,
19 pub flags: OpenFlags,
20 pub journal_mode: JournalMode,
21 pub synchronous_mode: SynchronousMode,
22 pub temp_store: TempStore,
23 pub cache_size: u32,
24 pub wal_autocheckpoint: u32,
25 pub page_size: u32, pub mmap_size: u64, }
28
29impl SqliteConfig {
30 pub fn new<P: AsRef<Path>>(path: P) -> Self {
32 Self {
33 path: DbPath::File(path.as_ref().to_path_buf()),
34 flags: OpenFlags::default(),
35 journal_mode: JournalMode::Wal,
36 synchronous_mode: SynchronousMode::Normal,
37 temp_store: TempStore::Memory,
38 cache_size: 20000,
39 wal_autocheckpoint: 1000,
40 page_size: 4096, mmap_size: 0, }
43 }
44
45 pub fn safe<P: AsRef<Path>>(path: P) -> Self {
51 Self {
52 path: DbPath::File(path.as_ref().to_path_buf()),
53 flags: OpenFlags::default(),
54 journal_mode: JournalMode::Wal,
55 synchronous_mode: SynchronousMode::Full,
56 temp_store: TempStore::File,
57 cache_size: 20000,
58 wal_autocheckpoint: 1000,
59 page_size: 4096, mmap_size: 0, }
62 }
63
64 pub fn fast<P: AsRef<Path>>(path: P) -> Self {
70 Self {
71 path: DbPath::File(path.as_ref().to_path_buf()),
72 flags: OpenFlags::default(),
73 journal_mode: JournalMode::Memory,
74 synchronous_mode: SynchronousMode::Off,
75 temp_store: TempStore::Memory,
76 cache_size: 10000,
77 wal_autocheckpoint: 10000,
78 page_size: 16384, mmap_size: 268435456, }
81 }
82
83 pub fn tmpfs() -> Self {
91 Self {
92 path: DbPath::Tmpfs(PathBuf::from(format!("/tmp/reifydb_tmpfs_{}.db", Uuid::new_v4()))),
93 flags: OpenFlags::default(),
94 journal_mode: JournalMode::Wal,
95 synchronous_mode: SynchronousMode::Off,
96 temp_store: TempStore::Memory,
97 cache_size: 20000,
98 wal_autocheckpoint: 10000,
99 page_size: 16384, mmap_size: 268435456, }
102 }
103
104 pub fn in_memory() -> Self {
112 Self {
113 path: DbPath::Memory(PathBuf::from(format!("/dev/shm/reifydb_mem_{}.db", Uuid::new_v4()))),
114 flags: OpenFlags::default(),
115 journal_mode: JournalMode::Wal,
116 synchronous_mode: SynchronousMode::Off,
117 temp_store: TempStore::Memory,
118 cache_size: 20000,
119 wal_autocheckpoint: 10000,
120 page_size: 16384, mmap_size: 268435456, }
123 }
124
125 pub fn test() -> Self {
133 Self {
134 path: DbPath::Memory(PathBuf::from(format!("/dev/shm/reifydb_test_{}.db", Uuid::new_v4()))),
135 flags: OpenFlags::default(),
136 journal_mode: JournalMode::Wal,
137 synchronous_mode: SynchronousMode::Off,
138 temp_store: TempStore::Memory,
139 cache_size: 10000,
140 wal_autocheckpoint: 10000,
141 page_size: 4096, mmap_size: 0, }
144 }
145
146 pub fn path<P: AsRef<Path>>(mut self, path: P) -> Self {
148 self.path = DbPath::File(path.as_ref().to_path_buf());
149 self
150 }
151
152 pub fn flags(mut self, flags: OpenFlags) -> Self {
154 self.flags = flags;
155 self
156 }
157
158 pub fn journal_mode(mut self, mode: JournalMode) -> Self {
160 self.journal_mode = mode;
161 self
162 }
163
164 pub fn synchronous_mode(mut self, mode: SynchronousMode) -> Self {
166 self.synchronous_mode = mode;
167 self
168 }
169
170 pub fn temp_store(mut self, store: TempStore) -> Self {
172 self.temp_store = store;
173 self
174 }
175
176 pub fn cache_size(mut self, size_kb: u32) -> Self {
178 self.cache_size = size_kb;
179 self
180 }
181
182 pub fn wal_autocheckpoint(mut self, pages: u32) -> Self {
184 self.wal_autocheckpoint = pages;
185 self
186 }
187
188 pub fn page_size(mut self, size: u32) -> Self {
192 self.page_size = size;
193 self
194 }
195
196 pub fn mmap_size(mut self, size: u64) -> Self {
199 self.mmap_size = size;
200 self
201 }
202}
203
204impl Default for SqliteConfig {
205 fn default() -> Self {
206 Self::new("reify.reifydb")
207 }
208}
209
210#[derive(Debug, Clone)]
212pub struct OpenFlags {
213 pub read_write: bool,
214 pub create: bool,
215 pub full_mutex: bool,
216 pub no_mutex: bool,
217 pub shared_cache: bool,
218 pub private_cache: bool,
219 pub uri: bool,
220}
221
222impl OpenFlags {
223 pub fn new() -> Self {
225 Self::default()
226 }
227
228 pub fn read_write(mut self, enabled: bool) -> Self {
230 self.read_write = enabled;
231 self
232 }
233
234 pub fn create(mut self, enabled: bool) -> Self {
236 self.create = enabled;
237 self
238 }
239
240 pub fn full_mutex(mut self, enabled: bool) -> Self {
242 self.full_mutex = enabled;
243 self.no_mutex = !enabled;
244 self
245 }
246
247 pub fn no_mutex(mut self, enabled: bool) -> Self {
249 self.no_mutex = enabled;
250 self.full_mutex = !enabled;
251 self
252 }
253
254 pub fn shared_cache(mut self, enabled: bool) -> Self {
256 self.shared_cache = enabled;
257 self.private_cache = !enabled;
258 self
259 }
260
261 pub fn private_cache(mut self, enabled: bool) -> Self {
263 self.private_cache = enabled;
264 self.shared_cache = !enabled;
265 self
266 }
267
268 pub fn uri(mut self, enabled: bool) -> Self {
270 self.uri = enabled;
271 self
272 }
273}
274
275impl Default for OpenFlags {
276 fn default() -> Self {
277 Self {
278 read_write: true,
279 create: true,
280 full_mutex: true,
281 no_mutex: false,
282 shared_cache: false,
283 private_cache: false,
284 uri: false,
285 }
286 }
287}
288
289#[derive(Debug, Clone, Copy, PartialEq, Eq)]
291pub enum JournalMode {
292 Delete,
294 Truncate,
296 Persist,
298 Memory,
300 Wal,
302 Off,
304}
305
306impl JournalMode {
307 pub(crate) fn as_str(&self) -> &'static str {
308 match self {
309 JournalMode::Delete => "DELETE",
310 JournalMode::Truncate => "TRUNCATE",
311 JournalMode::Persist => "PERSIST",
312 JournalMode::Memory => "MEMORY",
313 JournalMode::Wal => "WAL",
314 JournalMode::Off => "OFF",
315 }
316 }
317}
318
319#[derive(Debug, Clone, Copy, PartialEq, Eq)]
321pub enum SynchronousMode {
322 Off,
324 Normal,
326 Full,
328 Extra,
330}
331
332impl SynchronousMode {
333 pub(crate) fn as_str(&self) -> &'static str {
334 match self {
335 SynchronousMode::Off => "OFF",
336 SynchronousMode::Normal => "NORMAL",
337 SynchronousMode::Full => "FULL",
338 SynchronousMode::Extra => "EXTRA",
339 }
340 }
341}
342
343#[derive(Debug, Clone, Copy, PartialEq, Eq)]
345pub enum TempStore {
346 Default,
348 File,
350 Memory,
352}
353
354impl TempStore {
355 pub(crate) fn as_str(&self) -> &'static str {
356 match self {
357 TempStore::Default => "DEFAULT",
358 TempStore::File => "FILE",
359 TempStore::Memory => "MEMORY",
360 }
361 }
362}
363
364#[cfg(test)]
365pub mod tests {
366 use reifydb_testing::tempdir::temp_dir;
367
368 use super::*;
369
370 #[test]
371 fn test_config_fluent_api() {
372 let config = SqliteConfig::new("/tmp/test.reifydb")
373 .journal_mode(JournalMode::Wal)
374 .synchronous_mode(SynchronousMode::Normal)
375 .temp_store(TempStore::Memory)
376 .cache_size(30000)
377 .flags(OpenFlags::new().read_write(true).create(true).full_mutex(true));
378
379 assert_eq!(config.path, DbPath::File(PathBuf::from("/tmp/test.reifydb")));
380 assert_eq!(config.journal_mode, JournalMode::Wal);
381 assert_eq!(config.synchronous_mode, SynchronousMode::Normal);
382 assert_eq!(config.temp_store, TempStore::Memory);
383 assert_eq!(config.cache_size, 30000);
384 assert!(config.flags.read_write);
385 assert!(config.flags.create);
386 assert!(config.flags.full_mutex);
387 }
388
389 #[test]
390 fn test_enum_string_conversion() {
391 assert_eq!(JournalMode::Wal.as_str(), "WAL");
392 assert_eq!(SynchronousMode::Normal.as_str(), "NORMAL");
393 assert_eq!(TempStore::Memory.as_str(), "MEMORY");
394 }
395
396 #[test]
397 fn test_all_journal_modes() {
398 assert_eq!(JournalMode::Delete.as_str(), "DELETE");
399 assert_eq!(JournalMode::Truncate.as_str(), "TRUNCATE");
400 assert_eq!(JournalMode::Persist.as_str(), "PERSIST");
401 assert_eq!(JournalMode::Memory.as_str(), "MEMORY");
402 assert_eq!(JournalMode::Wal.as_str(), "WAL");
403 assert_eq!(JournalMode::Off.as_str(), "OFF");
404 }
405
406 #[test]
407 fn test_all_synchronous_modes() {
408 assert_eq!(SynchronousMode::Off.as_str(), "OFF");
409 assert_eq!(SynchronousMode::Normal.as_str(), "NORMAL");
410 assert_eq!(SynchronousMode::Full.as_str(), "FULL");
411 assert_eq!(SynchronousMode::Extra.as_str(), "EXTRA");
412 }
413
414 #[test]
415 fn test_all_temp_store_modes() {
416 assert_eq!(TempStore::Default.as_str(), "DEFAULT");
417 assert_eq!(TempStore::File.as_str(), "FILE");
418 assert_eq!(TempStore::Memory.as_str(), "MEMORY");
419 }
420
421 #[test]
422 fn test_default_config() {
423 let config = SqliteConfig::default();
424 assert_eq!(config.path, DbPath::File(PathBuf::from("reify.reifydb")));
425 assert_eq!(config.journal_mode, JournalMode::Wal);
426 assert_eq!(config.synchronous_mode, SynchronousMode::Normal);
427 assert_eq!(config.temp_store, TempStore::Memory);
428 }
429
430 #[test]
431 fn test_safe_config() {
432 temp_dir(|db_path| {
433 let db_file = db_path.join("safe.reifydb");
434 let config = SqliteConfig::safe(&db_file);
435
436 assert_eq!(config.path, DbPath::File(db_file));
437 assert_eq!(config.journal_mode, JournalMode::Wal);
438 assert_eq!(config.synchronous_mode, SynchronousMode::Full);
439 assert_eq!(config.temp_store, TempStore::File);
440 Ok(())
441 })
442 .expect("test failed");
443 }
444
445 #[test]
446 fn test_fast_config() {
447 temp_dir(|db_path| {
448 let db_file = db_path.join("fast.reifydb");
449 let config = SqliteConfig::fast(&db_file);
450
451 assert_eq!(config.path, DbPath::File(db_file));
452 assert_eq!(config.journal_mode, JournalMode::Memory);
453 assert_eq!(config.synchronous_mode, SynchronousMode::Off);
454 assert_eq!(config.temp_store, TempStore::Memory);
455 Ok(())
456 })
457 .expect("test failed");
458 }
459
460 #[test]
461 fn test_tmpfs_config() {
462 let config = SqliteConfig::tmpfs();
463
464 match config.path {
466 DbPath::Tmpfs(path) => {
467 assert!(path.to_string_lossy().starts_with("/tmp/reifydb_tmpfs_"));
468 assert!(path.to_string_lossy().ends_with(".db"));
469 }
470 _ => panic!("Expected DbPath::Tmpfs variant"),
471 }
472
473 assert_eq!(config.journal_mode, JournalMode::Wal);
474 assert_eq!(config.synchronous_mode, SynchronousMode::Off);
475 assert_eq!(config.temp_store, TempStore::Memory);
476 assert_eq!(config.cache_size, 20000);
477 assert_eq!(config.wal_autocheckpoint, 10000);
478 }
479
480 #[test]
481 fn test_config_chaining() {
482 temp_dir(|db_path| {
483 let db_file = db_path.join("chain.reifydb");
484
485 let config = SqliteConfig::new(&db_file)
486 .journal_mode(JournalMode::Delete)
487 .synchronous_mode(SynchronousMode::Extra)
488 .temp_store(TempStore::File)
489 .flags(OpenFlags::new().read_write(false).create(false).shared_cache(true));
490
491 assert_eq!(config.journal_mode, JournalMode::Delete);
492 assert_eq!(config.synchronous_mode, SynchronousMode::Extra);
493 assert_eq!(config.temp_store, TempStore::File);
494 assert!(!config.flags.read_write);
495 assert!(!config.flags.create);
496 assert!(config.flags.shared_cache);
497 Ok(())
498 })
499 .expect("test failed");
500 }
501
502 #[test]
503 fn test_open_flags_mutex_exclusivity() {
504 let flags = OpenFlags::new().full_mutex(true);
505 assert!(flags.full_mutex);
506 assert!(!flags.no_mutex);
507
508 let flags = OpenFlags::new().no_mutex(true);
509 assert!(!flags.full_mutex);
510 assert!(flags.no_mutex);
511 }
512
513 #[test]
514 fn test_open_flags_cache_exclusivity() {
515 let flags = OpenFlags::new().shared_cache(true);
516 assert!(flags.shared_cache);
517 assert!(!flags.private_cache);
518
519 let flags = OpenFlags::new().private_cache(true);
520 assert!(!flags.shared_cache);
521 assert!(flags.private_cache);
522 }
523
524 #[test]
525 fn test_open_flags_all_combinations() {
526 let flags =
527 OpenFlags::new().read_write(true).create(true).full_mutex(true).shared_cache(true).uri(true);
528
529 assert!(flags.read_write);
530 assert!(flags.create);
531 assert!(flags.full_mutex);
532 assert!(!flags.no_mutex);
533 assert!(flags.shared_cache);
534 assert!(!flags.private_cache);
535 assert!(flags.uri);
536 }
537
538 #[test]
539 fn test_path_handling() {
540 temp_dir(|db_path| {
541 let file_path = db_path.join("test.reifydb");
543 let config = SqliteConfig::new(&file_path);
544 assert_eq!(config.path, DbPath::File(file_path));
545
546 let config = SqliteConfig::new(db_path);
548 assert_eq!(config.path, DbPath::File(db_path.to_path_buf()));
549 Ok(())
550 })
551 .expect("test failed");
552 }
553}