96 lines
2.4 KiB
Rust
96 lines
2.4 KiB
Rust
//! Database abstraction layer used by Hermit.
|
|
|
|
use std::borrow::Borrow;
|
|
use std::{marker::PhantomData, pin::Pin};
|
|
|
|
use crate::{ Id, Result, ap::Actor, err };
|
|
use futures::prelude::*;
|
|
use sqlx::{Executor, pool::PoolConnection, database::HasArguments};
|
|
use sqlx::{ FromRow, Either::Right };
|
|
|
|
/// `const ()` but in Rust
|
|
fn void <T> (_: T) -> () { () }
|
|
|
|
pub(crate) type Database = sqlx::Postgres;
|
|
|
|
/// Specifies how to connect to the database.
|
|
pub struct Config {}
|
|
|
|
/// A database client.
|
|
///
|
|
/// Cloning this client is cheap.
|
|
#[derive(Clone)]
|
|
pub struct Client {
|
|
/// The internal connection pool.
|
|
pool: sqlx::Pool<Database>,
|
|
}
|
|
|
|
impl Client {
|
|
|
|
/// Attempt to connect to the database using the provided configuration.
|
|
pub async fn new (_: Config) -> Result<Client> {
|
|
todo!()
|
|
}
|
|
|
|
pub async fn query <'e, T: 'e> (&self, query: Query<'e, T>) -> Pin<Box<dyn Stream<Item = Result<T>> + Send + 'e>> {
|
|
let Query (q, f) = query;
|
|
let stream = q
|
|
.fetch_many(&self.pool)
|
|
.filter_map(async move |r| {
|
|
match r.map_err(err) {
|
|
Ok (Right (row)) => Some (f(row)),
|
|
Err (error) => Some (Err (error)),
|
|
_ => None,
|
|
}
|
|
});
|
|
|
|
Box::pin (stream)
|
|
}
|
|
|
|
pub async fn get <'q, T: 'q> (&self, query: Query<'q, T>) -> Result<Option<T>> {
|
|
self.query(query)
|
|
.await
|
|
.next()
|
|
.await
|
|
.transpose()
|
|
}
|
|
|
|
/// Handles the getting-a-connection logic.
|
|
async fn with_conn <F, O, T> (&self, f: F) -> Result<T>
|
|
where
|
|
F: FnOnce (&mut PoolConnection<Database>) -> O,
|
|
O: Future<Output = Result<T>>,
|
|
{
|
|
self.pool
|
|
.acquire()
|
|
.map_err(err)
|
|
.and_then(|mut c| {
|
|
f(&mut c)
|
|
})
|
|
.await
|
|
}
|
|
|
|
}
|
|
|
|
type Q<'a> = sqlx::query::Query<'a, Database, <Database as HasArguments<'a>>::Arguments>;
|
|
type Row = <Database as sqlx::Database>::Row;
|
|
|
|
pub struct Query <'a, T> (Q<'a>, fn (Row) -> Result<T>);
|
|
|
|
/// Generate a query that gets an [`Actor`] by its [`Id`].
|
|
pub fn get_actor <'a> (id: &'a Id) -> Query<'a, Actor> {
|
|
|
|
// Prepare a query
|
|
let query = sqlx::query("select * from actors where id = $1")
|
|
.bind(id);
|
|
|
|
// Return an sql query which will result in a series of rows,
|
|
// and a decoder function that will translate each row to a
|
|
// value of type `Actor`.
|
|
Query (query, |row: Row| {
|
|
let data = Actor::from_row(&row)?;
|
|
Ok (data)
|
|
})
|
|
|
|
}
|