use crate::error::{DatabaseError, Error};
use postrust_sql::SqlParam;
use std::future::Future;
#[cfg(feature = "pg-socket")]
mod socket;
#[cfg(feature = "pg-socket")]
pub use socket::{Connector, PgSocketDb, SocketTx};
#[cfg(feature = "pg-socket")]
pub use tokio_postgres;
#[cfg(feature = "sqlx")]
mod sqlx_impl;
#[cfg(feature = "sqlx")]
pub use sqlx_impl::SqlxTx;
#[cfg(not(target_arch = "wasm32"))]
pub trait MaybeSend: Send {}
#[cfg(not(target_arch = "wasm32"))]
impl<T: Send + ?Sized> MaybeSend for T {}
#[cfg(target_arch = "wasm32")]
pub trait MaybeSend {}
#[cfg(target_arch = "wasm32")]
impl<T: ?Sized> MaybeSend for T {}
#[cfg(not(target_arch = "wasm32"))]
pub trait MaybeSync: Sync {}
#[cfg(not(target_arch = "wasm32"))]
impl<T: Sync + ?Sized> MaybeSync for T {}
#[cfg(target_arch = "wasm32")]
pub trait MaybeSync {}
#[cfg(target_arch = "wasm32")]
impl<T: ?Sized> MaybeSync for T {}
#[cfg(not(target_arch = "wasm32"))]
pub type BoxFuture<'a, T> = std::pin::Pin<Box<dyn Future<Output = T> + Send + 'a>>;
#[cfg(target_arch = "wasm32")]
pub type BoxFuture<'a, T> = std::pin::Pin<Box<dyn Future<Output = T> + 'a>>;
pub trait Db: MaybeSend + MaybeSync {
type Tx<'a>: Tx
where
Self: 'a;
fn begin(&self) -> impl Future<Output = Result<Self::Tx<'_>, DbError>> + MaybeSend;
}
pub trait Tx: MaybeSend {
fn execute(
&mut self,
sql: &str,
params: &[SqlParam],
) -> impl Future<Output = Result<u64, DbError>> + MaybeSend;
fn fetch_json(
&mut self,
sql: &str,
params: &[SqlParam],
) -> impl Future<Output = Result<Vec<serde_json::Value>, DbError>> + MaybeSend;
fn fetch_values(
&mut self,
sql: &str,
params: &[SqlParam],
) -> impl Future<Output = Result<Vec<Vec<serde_json::Value>>, DbError>> + MaybeSend;
fn fetch_raw(
&mut self,
sql: &str,
params: &[SqlParam],
count_column: &str,
) -> impl Future<Output = Result<RawRows, DbError>> + MaybeSend;
fn commit(self) -> impl Future<Output = Result<(), DbError>> + MaybeSend;
fn queue(&mut self, sql: &str, params: &[SqlParam]);
fn fetch_json_then(
&mut self,
sql: &str,
params: &[SqlParam],
then: &str,
) -> impl Future<Output = FetchedThen> + MaybeSend;
fn fetch_json_then_and_commit(
mut self,
sql: &str,
params: &[SqlParam],
then: &str,
) -> impl Future<Output = FetchedThen> + MaybeSend
where
Self: Sized,
{
async move {
let fetched = self.fetch_json_then(sql, params, then).await?;
self.commit().await?;
Ok(fetched)
}
}
fn fetch_json_and_commit(
self,
sql: &str,
params: &[SqlParam],
) -> impl Future<Output = Result<Vec<serde_json::Value>, DbError>> + MaybeSend;
}
pub type FetchedThen = Result<(Vec<serde_json::Value>, Vec<Vec<serde_json::Value>>), DbError>;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct RawRows {
pub rows: usize,
pub first: Option<Vec<u8>>,
pub count: Option<i64>,
}
impl RawRows {
pub fn new(rows: usize, first: Option<Vec<u8>>, count: Option<i64>) -> Self {
Self { rows, first, count }
}
}
#[derive(Debug)]
pub struct DbError {
message: String,
database: Option<Box<DatabaseError>>,
connection: bool,
queued: bool,
}
impl DbError {
pub fn other(message: impl Into<String>) -> Self {
Self {
message: message.into(),
database: None,
connection: false,
queued: false,
}
}
pub fn connection(message: impl Into<String>) -> Self {
Self {
connection: true,
..Self::other(message)
}
}
pub fn database(message: impl Into<String>, error: DatabaseError) -> Self {
Self {
database: Some(Box::new(error)),
..Self::other(message)
}
}
pub fn queued(self) -> Self {
Self {
queued: true,
..self
}
}
pub fn as_database(&self) -> Option<&DatabaseError> {
self.database.as_deref()
}
pub fn is_connection(&self) -> bool {
self.connection
}
pub fn was_queued(&self) -> bool {
self.queued
}
}
impl std::fmt::Display for DbError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.message)
}
}
impl std::error::Error for DbError {}
impl From<DbError> for Error {
fn from(error: DbError) -> Self {
match error.database {
Some(database) => Error::Database(*database),
None if error.connection => Error::ConnectionPool(error.message),
None => Error::Internal(error.message),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_broken_connection_is_reported_as_one() {
let error = Error::from(DbError::connection("connection closed").queued());
assert!(matches!(error, Error::ConnectionPool(_)), "{error:?}");
let error = Error::from(DbError::other("could not decode"));
assert!(matches!(error, Error::Internal(_)), "{error:?}");
}
}