1use crate::error::Error;
4use sqlx::migrate::Migrator;
5pub use sqlx::sqlite::{SqliteConnectOptions, SqlitePool};
6use sqlx::sqlite::{SqlitePoolOptions, SqliteSynchronous};
7use sqlx::ConnectOptions;
8use std::time::Duration;
9use tracing::{error, info, warn};
10
11static MIGRATOR: Migrator = sqlx::migrate!();
13
14async fn get_file_pool(
15 filename: &std::path::Path,
16 db_connections: u32,
17 timeout: Duration,
18) -> Result<SqlitePool, Error> {
19 let slow_log = Duration::from_secs_f32(0.3);
28
29 info!(
30 "Opening SQLite database file: {}, connections={}, timeout={}",
31 filename.display(),
32 db_connections,
33 timeout.as_secs_f32()
34 );
35 let conn = SqliteConnectOptions::new()
36 .filename(filename)
37 .synchronous(SqliteSynchronous::Normal)
38 .shared_cache(false)
39 .pragma("temp_store", "memory")
40 .log_statements(log::LevelFilter::Trace)
41 .log_slow_statements(log::LevelFilter::Warn, slow_log);
42
43 let pool = SqlitePoolOptions::new()
44 .min_connections(db_connections)
45 .max_connections(db_connections)
46 .test_before_acquire(false)
47 .acquire_timeout(timeout)
48 .connect_with(conn)
49 .await?;
50 Ok(pool)
51}
52
53pub async fn run_migrations(pool: &SqlitePool) -> Result<(), Error> {
54 MIGRATOR.run(pool).await?;
55 Ok(())
56}
57
58pub struct SqlitePoolBuilder<'tempfile> {
60 path: Option<&'tempfile std::path::Path>,
61 migrate: bool,
62 db_connections: u32,
63 timeout: Duration,
64}
65
66impl<'tempfile> SqlitePoolBuilder<'tempfile> {
67 #[must_use]
68 pub const fn new() -> Self {
69 Self {
70 path: None,
71 migrate: true,
72 db_connections: 4,
73 timeout: Duration::from_secs(5),
76 }
77 }
78
79 #[must_use]
85 pub const fn db_path(mut self, path: &'tempfile std::path::Path) -> Self {
86 self.path = Some(path);
87 self
88 }
89 #[must_use]
92 pub const fn db_connections(mut self, db_connections: Option<u32>) -> Self {
93 if let Some(conns) = db_connections {
94 self.db_connections = conns;
95 }
96 self
97 }
98 #[must_use]
99 pub const fn db_timeout(mut self, db_timeout: Option<u32>) -> Self {
100 if let Some(timeout) = db_timeout {
101 assert!(
102 timeout <= 25,
103 "DBus timeout is ~25s. timeout should be less."
104 );
105 self.timeout = Duration::from_secs(timeout as u64);
106 }
107 self
108 }
109 #[must_use]
110 pub const fn migrate(mut self, migrate: bool) -> Self {
111 self.migrate = migrate;
112 self
113 }
114
115 pub async fn build(self) -> Result<SqlitePool, Error> {
116 let db_path = self.path.expect("Must have a path");
117 if self.db_connections < 2 {
118 error!(
119 "Too few connections to function. conns = {} < 2",
120 self.db_connections
121 );
122 }
123 let pool = get_file_pool(db_path, self.db_connections, self.timeout).await?;
124
125 if self.migrate {
126 warn!("Running migrations on {:?}", &db_path);
127 run_migrations(&pool).await?;
128 }
129 Ok(pool)
130 }
131}
132
133impl Default for SqlitePoolBuilder<'_> {
134 fn default() -> Self {
135 Self::new()
136 }
137}