sinan 0.1.0

A Boilerplate for Rapid Axum Web Service Deployment.
Documentation
use std::any::type_name;
use std::time::Duration;

use eyre::{OptionExt, Result};
use sea_orm::{ConnectOptions, DatabaseConnection};
use tracing::log::LevelFilter;

use crate::contracts::{Application, Service};
use crate::foundation::application::config;
use crate::services::{Redis, service};

async fn connection() -> Result<DatabaseConnection> {
    let config = config()?.get::<crate::config::Database>("database")?;

    let mut opt = ConnectOptions::new(config.uri);

    opt.max_connections(config.max_connections)
        .min_connections(config.min_connections)
        .connect_timeout(Duration::from_secs(config.connect_timeout))
        .acquire_timeout(Duration::from_secs(config.acquire_timeout.unwrap_or(8)))
        .idle_timeout(Duration::from_secs(config.idle_timeout))
        .max_lifetime(Duration::from_secs(config.max_lifetime))
        .sqlx_logging(config.enable_logging)
        .sqlx_logging_level(LevelFilter::Debug);

    Ok(sea_orm::Database::connect(opt).await?)
}

pub struct Database(DatabaseConnection);

impl Service for Database {
    fn register<A: Application + ?Sized>() -> Self
    where
        Self: Sized,
    {
        let connection = A::runtime()
            .block_on(connection())
            .unwrap_or_else(|err| panic!("{}", err));

        tracing::info!("[service] {} booting", type_name::<Self>());

        Database(connection)
    }

    fn boot<A: Application + ?Sized>() -> Result<()>
    where
        Self: Sized,
    {
        let db = service()
            .get::<Database>()
            .ok_or_eyre("failed to get db service")?;

        A::runtime().block_on(db.ping())?;

        tracing::info!("[service] {} booted", type_name::<Self>());

        Ok(())
    }
}

impl Database {
    pub fn connection(&self) -> &DatabaseConnection {
        &self.0
    }

    pub async fn ping(&self) -> Result<()> {
        assert!(self.connection().ping().await.is_ok());

        Ok(())
    }
}