feedsignal/crates/db/src/lib.rs

54 lines
2 KiB
Rust
Raw Normal View History

mod affinities;
mod articles;
mod feeds;
mod models;
mod reading_events;
pub use models::RankedArticleRow;
pub(crate) mod schema;
use anyhow::Result;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use diesel_async::pooled_connection::bb8::Pool;
use diesel_async::pooled_connection::AsyncDieselConnectionManager;
use diesel_async::sync_connection_wrapper::SyncConnectionWrapper;
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
const MIGRATIONS: EmbeddedMigrations = embed_migrations!("./migrations");
/// SQLite has no native async driver, so `diesel-async` wraps a blocking
/// `SqliteConnection` and runs it on a blocking thread under the hood.
type AsyncSqliteConnection = SyncConnectionWrapper<SqliteConnection>;
#[derive(Clone)]
pub struct Db {
pool: Pool<AsyncSqliteConnection>,
}
impl Db {
/// `database_url` is a plain SQLite file path (e.g. "feedsignal.db"),
/// created if it doesn't exist. Everything lives in one file, no
/// separate database server to run.
pub async fn connect(database_url: &str) -> Result<Self> {
// `MigrationHarness` only works on a synchronous `Connection`, and
// this only runs once at boot, so open a throwaway sync connection
// just for it rather than pulling migrations through the async pool.
// Each pending migration runs in its own transaction (SQLite
// supports transactional DDL), and `down.sql` gives each one a
// matching revert.
let url = database_url.to_string();
tokio::task::spawn_blocking(move || -> Result<()> {
let mut conn = SqliteConnection::establish(&url)?;
conn.run_pending_migrations(MIGRATIONS)
.map_err(|e| anyhow::anyhow!(e))?;
Ok(())
})
.await??;
let manager = AsyncDieselConnectionManager::<AsyncSqliteConnection>::new(database_url);
let pool = Pool::builder().max_size(5).build(manager).await?;
Ok(Self { pool })
}
}