mod disabled_commands;
mod kv_store;
pub mod model;
mod reddit_embed;
mod tic_tac_toe;
mod tiktok_embed;
pub use self::tic_tac_toe::{
TicTacToeCreateGameError,
TicTacToeTryMoveError,
TicTacToeTryMoveResponse,
};
use anyhow::Context;
use camino::{
Utf8Path,
Utf8PathBuf,
};
use once_cell::sync::Lazy;
use std::{
os::raw::c_int,
sync::Arc,
};
use tracing::{
error,
warn,
};
const SETUP_TABLES_SQL: &str = include_str!("../sql/setup_tables.sql");
static LOGGER_INIT: Lazy<Result<(), Arc<rusqlite::Error>>> = Lazy::new(|| {
unsafe { rusqlite::trace::config_log(Some(sqlite_logger_func)).map_err(Arc::new) }
});
fn sqlite_logger_func(error_code: c_int, msg: &str) {
warn!("sqlite error code ({}): {}", error_code, msg);
}
#[derive(Clone, Debug)]
pub struct Database {
db: async_rusqlite::Database,
}
impl Database {
pub async unsafe fn new<P>(path: P, create_if_missing: bool) -> anyhow::Result<Self>
where
P: Into<Utf8PathBuf>,
{
let path = path.into();
tokio::task::spawn_blocking(move || Self::blocking_new(&path, create_if_missing))
.await
.context("failed to join tokio task")?
}
pub unsafe fn blocking_new<P>(path: P, create_if_missing: bool) -> anyhow::Result<Self>
where
P: AsRef<Utf8Path>,
{
LOGGER_INIT
.clone()
.context("failed to init sqlite logger")?;
let db = async_rusqlite::Database::blocking_open(path.as_ref(), create_if_missing, |db| {
db.execute_batch(SETUP_TABLES_SQL)
.context("failed to setup database")?;
Ok(())
})
.context("failed to open database")?;
Ok(Database { db })
}
async fn access_db<F, R>(&self, func: F) -> anyhow::Result<R>
where
F: FnOnce(&mut rusqlite::Connection) -> R + Send + 'static,
R: Send + 'static,
{
Ok(self.db.access_db(move |db| func(db)).await?)
}
pub async fn close(&self) -> anyhow::Result<()> {
if let Err(e) = self
.db
.access_db(|db| {
db.execute("PRAGMA OPTIMIZE;", [])?;
db.execute("VACUUM;", [])
})
.await
.context("failed to access db")
.and_then(|v| v.context("failed to execute shutdown commands"))
{
error!("{}", e);
}
self.db
.close()
.await
.context("failed to send close request to db")?;
self.db.join().await.context("failed to join db thread")?;
Ok(())
}
}