1
0
Fork 0
mirror of https://git.alemi.dev/guestbook.rs.git synced 2025-03-15 08:45:32 +01:00
guestbook.rs/src/storage.rs
alemi 0afc79934d
feat: add sql drivers with sqlx
had to compromise on a lot of stuff because sqlx 0.7+ broke the AnyDb
driver, but ehhh it works
2024-01-03 03:32:03 +01:00

88 lines
2.5 KiB
Rust

use chrono::Utc;
use crate::{model::{PageView, PageInsertion, Page}, config::ConfigOverrides};
#[derive(Debug)]
pub struct StorageProvider {
db: sqlx::Pool<sqlx::Any>,
overrides: ConfigOverrides,
}
// TODO bool type is not supported in Any driver?????
// so the `public` field is an integer which is ridicolous
// but literally cannot get it to work ffs
//
// https://github.com/launchbadge/sqlx/issues/2778
const SQLITE_SCHEMA : &str = "
CREATE TABLE IF NOT EXISTS pages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
author VARCHAR NOT NULL,
contact VARCHAR,
body VARCHAR NOT NULL,
timestamp INTEGER NOT NULL,
public INTEGER NOT NULL
);
";
const POSTGRES_SCHEMA : &str = "
CREATE TABLE IF NOT EXISTS pages (
id SERIAL PRIMARY KEY,
author TEXT NOT NULL,
contact TEXT,
body TEXT NOT NULL,
timestamp INTEGER NOT NULL,
public INTEGER NOT NULL
);
";
impl StorageProvider {
pub async fn connect(dest: &str, overrides: ConfigOverrides) -> sqlx::Result<Self> {
let db = sqlx::AnyPool::connect(dest).await?;
match db.acquire().await?.backend_name() {
"PostgreSQL" => { sqlx::query(POSTGRES_SCHEMA).execute(&db).await?; },
"SQLite" => { sqlx::query(SQLITE_SCHEMA).execute(&db).await?; },
"MySQL" => { sqlx::query(SQLITE_SCHEMA).execute(&db).await?; }, // TODO will this work?
_ => tracing::warn!("could not ensure schema: unsupported database type"),
}
Ok(StorageProvider { db, overrides })
}
pub async fn archive(&self, mut page: PageInsertion) -> sqlx::Result<Page> {
page.sanitize();
page.overrides(&self.overrides);
let result = sqlx::query("INSERT INTO pages (author, contact, body, timestamp, public) VALUES ($1, $2, $3, $4, $5)")
.bind(page.author.as_deref().unwrap_or("anonymous").to_string())
.bind(page.contact.clone())
.bind(page.body.clone())
.bind(page.date.unwrap_or(Utc::now()).timestamp())
.bind(if page.public.unwrap_or(true) { 1 } else { 0 })
.execute(&self.db)
.await?;
Ok(
Page {
id: result.last_insert_id().unwrap_or(-1),
author: page.author.unwrap_or("anonymous".into()),
contact: page.contact,
body: page.body,
timestamp: page.date.unwrap_or(Utc::now()).timestamp(),
public: page.public.unwrap_or(true),
}
)
}
pub async fn extract(&self, offset: i32, window: i32) -> sqlx::Result<Vec<PageView>> {
// TODO since AnyPool won't handle booleans we compare with an integer
let out = sqlx::query_as("SELECT * FROM pages WHERE public = 1 LIMIT $1 OFFSET $2")
.bind(window)
.bind(offset)
.fetch_all(&self.db)
.await?
.iter()
.map(PageView::from)
.collect();
Ok(out)
}
}