use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SynchronousMode {
Off,
#[default]
Normal,
Full,
Extra,
}
impl SynchronousMode {
pub fn as_pragma(&self) -> &'static str {
match self {
Self::Off => "OFF",
Self::Normal => "NORMAL",
Self::Full => "FULL",
Self::Extra => "EXTRA",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum JournalMode {
Delete,
Truncate,
Persist,
Memory,
#[default]
Wal,
Off,
}
impl JournalMode {
pub fn as_pragma(&self) -> &'static str {
match self {
Self::Delete => "DELETE",
Self::Truncate => "TRUNCATE",
Self::Persist => "PERSIST",
Self::Memory => "MEMORY",
Self::Wal => "WAL",
Self::Off => "OFF",
}
}
}
#[derive(Debug, Clone)]
pub struct SqliteConfig {
pub path: Option<PathBuf>,
pub synchronous: SynchronousMode,
pub journal_mode: JournalMode,
pub foreign_keys: bool,
pub busy_timeout_ms: u32,
pub cache_size: i32,
pub page_size: u32,
pub read_only: bool,
pub create_if_missing: bool,
}
impl Default for SqliteConfig {
fn default() -> Self {
Self {
path: None,
synchronous: SynchronousMode::Normal,
journal_mode: JournalMode::Wal,
foreign_keys: true,
busy_timeout_ms: 5000,
cache_size: -2000, page_size: 4096,
read_only: false,
create_if_missing: true,
}
}
}
impl SqliteConfig {
pub fn memory() -> Self {
Self {
path: None,
journal_mode: JournalMode::Memory,
..Default::default()
}
}
pub fn file(path: impl Into<PathBuf>) -> Self {
Self {
path: Some(path.into()),
..Default::default()
}
}
pub fn performance() -> Self {
Self {
synchronous: SynchronousMode::Off,
journal_mode: JournalMode::Wal,
cache_size: -10000, ..Default::default()
}
}
pub fn durable() -> Self {
Self {
synchronous: SynchronousMode::Full,
journal_mode: JournalMode::Wal,
foreign_keys: true,
..Default::default()
}
}
pub fn with_path(mut self, path: impl Into<PathBuf>) -> Self {
self.path = Some(path.into());
self
}
pub fn with_synchronous(mut self, mode: SynchronousMode) -> Self {
self.synchronous = mode;
self
}
pub fn with_journal_mode(mut self, mode: JournalMode) -> Self {
self.journal_mode = mode;
self
}
pub fn with_foreign_keys(mut self, enabled: bool) -> Self {
self.foreign_keys = enabled;
self
}
pub fn with_busy_timeout(mut self, ms: u32) -> Self {
self.busy_timeout_ms = ms;
self
}
pub fn with_read_only(mut self, read_only: bool) -> Self {
self.read_only = read_only;
self
}
pub fn to_pragmas(&self) -> Vec<String> {
let mut pragmas = Vec::new();
pragmas.push(format!("PRAGMA page_size = {};", self.page_size));
pragmas.push(format!("PRAGMA journal_mode = {};", self.journal_mode.as_pragma()));
pragmas.push(format!("PRAGMA synchronous = {};", self.synchronous.as_pragma()));
pragmas.push(format!(
"PRAGMA foreign_keys = {};",
if self.foreign_keys { "ON" } else { "OFF" }
));
pragmas.push(format!("PRAGMA busy_timeout = {};", self.busy_timeout_ms));
pragmas.push(format!("PRAGMA cache_size = {};", self.cache_size));
pragmas
}
pub fn path_string(&self) -> String {
match &self.path {
Some(p) => p.to_string_lossy().to_string(),
None => ":memory:".to_string(),
}
}
pub fn is_memory(&self) -> bool {
self.path.is_none()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_config() {
let config = SqliteConfig::memory();
assert!(config.is_memory());
assert_eq!(config.path_string(), ":memory:");
}
#[test]
fn test_file_config() {
let config = SqliteConfig::file("/tmp/test.db");
assert!(!config.is_memory());
assert!(config.path_string().contains("test.db"));
}
#[test]
fn test_pragmas() {
let config = SqliteConfig::default();
let pragmas = config.to_pragmas();
assert!(pragmas.iter().any(|p| p.contains("journal_mode")));
assert!(pragmas.iter().any(|p| p.contains("synchronous")));
assert!(pragmas.iter().any(|p| p.contains("foreign_keys")));
}
#[test]
fn test_presets() {
let perf = SqliteConfig::performance();
assert_eq!(perf.synchronous, SynchronousMode::Off);
let durable = SqliteConfig::durable();
assert_eq!(durable.synchronous, SynchronousMode::Full);
}
}