ts-webapi 0.4.11

Library for my web API projects
Documentation
//! Setup a connection pool for `PostgreSQL`.

use core::{error::Error, fmt};
use std::path::PathBuf;

use bb8::{Pool, RunError};
use bb8_postgres::PostgresConnectionManager;
use tokio_postgres::{Error as PostgresError, NoTls};
use ts_sql::{MigrationError, perform_migrations_async};

/// Type alias for a `NoTLS` Postgres connection pool.
pub type ConnectionPool = Pool<PostgresConnectionManager<NoTls>>;

/// Error kinds for setting up Postgres.
#[derive(Debug)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum SetupPostgresError {
    #[non_exhaustive]
    /// The pool could not be built.
    BuildPoolError { source: PostgresError },

    #[non_exhaustive]
    /// The connection string provided was invalid.
    InvalidConnectionString { source: PostgresError },

    #[non_exhaustive]
    GetConnection { source: RunError<PostgresError> },

    #[non_exhaustive]
    PerformMigrations { source: MigrationError },
}
impl fmt::Display for SetupPostgresError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self {
            Self::BuildPoolError { .. } => {
                write!(f, "failed to build connection pool")
            }
            Self::InvalidConnectionString { .. } => {
                write!(f, "invalid connection string")
            }
            Self::GetConnection { .. } => write!(f, "could not connect to database"),
            Self::PerformMigrations { .. } => write!(f, "failed to perform migrations"),
        }
    }
}
impl Error for SetupPostgresError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match &self {
            Self::InvalidConnectionString { source } | Self::BuildPoolError { source } => {
                Some(source)
            }
            Self::GetConnection { source, .. } => Some(source),
            Self::PerformMigrations { source, .. } => Some(source),
        }
    }
}

/// Setup a connection pool for `PostgreSQL`.
///
/// Connection string should be in the form:
/// `postgres://username:password@host:port`
pub async fn setup_connection_pool<S: ToString>(
    connection_string: S,
    migrations_directory: Option<PathBuf>,
) -> Result<ConnectionPool, SetupPostgresError> {
    let manager = PostgresConnectionManager::new_from_stringlike(connection_string, NoTls)
        .map_err(|source| SetupPostgresError::InvalidConnectionString { source })?;

    let pool = Pool::builder()
        .build(manager)
        .await
        .map_err(|source| SetupPostgresError::BuildPoolError { source })?;

    {
        let client = pool
            .get()
            .await
            .map_err(|source| SetupPostgresError::GetConnection { source })?;

        perform_migrations_async(&client, migrations_directory)
            .await
            .map_err(|source| SetupPostgresError::PerformMigrations { source })?;
    }

    Ok(pool)
}