tideway 0.7.18

A batteries-included Rust web framework built on Axum for building SaaS applications quickly
Documentation
use sea_orm::DbErr;
use std::mem;
use std::sync::atomic::{AtomicU64, Ordering};
use testcontainers_modules::{
    postgres::Postgres,
    testcontainers::{ImageExt, core::ContainerAsync, runners::AsyncRunner},
};

#[derive(Debug)]
pub struct PostgresContainer {
    pub(crate) connection_url: String,
    container: Option<ContainerAsync<Postgres>>,
    keep_running: bool,
}

static CONTAINER_COUNTER: AtomicU64 = AtomicU64::new(0);

impl PostgresContainer {
    pub async fn start() -> Result<Self, DbErr> {
        let username =
            std::env::var("TIDEWAY_TEST_PG_USER").unwrap_or_else(|_| "postgres".to_string());
        let password =
            std::env::var("TIDEWAY_TEST_PG_PASSWORD").unwrap_or_else(|_| "postgres".to_string());
        let db_name = format!(
            "tideway_test_{}_{}",
            std::process::id(),
            CONTAINER_COUNTER.fetch_add(1, Ordering::SeqCst)
        );
        let keep_running = std::env::var("TIDEWAY_TEST_KEEP_CONTAINERS")
            .map(|value| value == "1" || value.eq_ignore_ascii_case("true"))
            .unwrap_or(false);
        let request = Postgres::default()
            .with_user(&username)
            .with_password(&password)
            .with_db_name(&db_name);
        let request = if let Ok(tag) = std::env::var("TIDEWAY_TEST_POSTGRES_IMAGE") {
            request.with_tag(tag)
        } else {
            request.into()
        };
        let container = request.start().await.map_err(|error| {
            DbErr::Custom(format!("Failed to start postgres container: {error}"))
        })?;
        let host = container
            .get_host()
            .await
            .map_err(|error| DbErr::Custom(format!("Failed to resolve postgres host: {error}")))?;
        let port = container.get_host_port_ipv4(5432).await.map_err(|error| {
            DbErr::Custom(format!("Failed to resolve postgres host port: {error}"))
        })?;

        let connection_url = format!(
            "postgres://{}:{}@{}:{}/{}",
            urlencoding::encode(&username),
            urlencoding::encode(&password),
            host,
            port,
            db_name
        );

        Ok(Self {
            connection_url,
            container: Some(container),
            keep_running,
        })
    }
}

impl Drop for PostgresContainer {
    fn drop(&mut self) {
        if self.keep_running {
            if let Some(container) = self.container.take() {
                mem::forget(container);
            }
            return;
        }
    }
}