1use std::str::FromStr;
4use std::time::Duration;
5
6use async_trait::async_trait;
7use sbol_db_core::DomainError;
8use sbol_db_storage::{MigrationEntry, Migrator};
9use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions};
10
11pub use sqlx::SqlitePool;
12
13static MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!("./migrations");
14
15pub(crate) fn db_err<E: std::fmt::Display>(e: E) -> DomainError {
16 DomainError::Database(e.to_string())
17}
18
19pub async fn connect(url: &str) -> Result<SqlitePool, DomainError> {
24 let options = SqliteConnectOptions::from_str(url)
25 .map_err(db_err)?
26 .create_if_missing(true)
27 .journal_mode(SqliteJournalMode::Wal)
28 .busy_timeout(Duration::from_secs(5))
29 .foreign_keys(true);
30 SqlitePoolOptions::new()
31 .max_connections(8)
32 .connect_with(options)
33 .await
34 .map_err(db_err)
35}
36
37pub async fn connect_and_migrate(url: &str) -> Result<SqlitePool, DomainError> {
39 let pool = connect(url).await?;
40 run_migrations(&pool).await?;
41 Ok(pool)
42}
43
44pub async fn run_migrations(pool: &SqlitePool) -> Result<(), DomainError> {
45 MIGRATOR.run(pool).await.map_err(db_err)
46}
47
48pub async fn migration_status(pool: &SqlitePool) -> Result<Vec<MigrationEntry>, DomainError> {
49 let known = MIGRATOR
50 .iter()
51 .map(|m| (m.version, m.description.to_string()))
52 .collect::<Vec<_>>();
53 let applied: Vec<i64> =
54 sqlx::query_scalar("SELECT version FROM _sqlx_migrations ORDER BY version")
55 .fetch_all(pool)
56 .await
57 .unwrap_or_default();
58 Ok(known
59 .into_iter()
60 .map(|(version, description)| MigrationEntry {
61 version,
62 description,
63 applied: applied.contains(&version),
64 })
65 .collect())
66}
67
68#[derive(Clone)]
70pub struct SqliteMigrator {
71 pool: SqlitePool,
72}
73
74impl SqliteMigrator {
75 pub fn new(pool: SqlitePool) -> Self {
76 Self { pool }
77 }
78}
79
80#[async_trait]
81impl Migrator for SqliteMigrator {
82 async fn run_migrations(&self) -> Result<(), DomainError> {
83 run_migrations(&self.pool).await
84 }
85
86 async fn migration_status(&self) -> Result<Vec<MigrationEntry>, DomainError> {
87 migration_status(&self.pool).await
88 }
89}