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