sinan 0.1.0

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

use bb8::{Pool, PooledConnection};
use bb8_redis::RedisConnectionManager;
use eyre::{eyre, OptionExt, Result};
use redis::cmd;
use tokio::sync::OnceCell;

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

static REDIS_POOL: OnceCell<Pool<RedisConnectionManager>> = OnceCell::const_new();

async fn connection() -> Result<Pool<RedisConnectionManager>> {
    let config = config()?.get::<crate::config::Redis>("redis")?;

    let manager = RedisConnectionManager::new(config.uri)?;
    let pool = Pool::builder()
        .min_idle(config.min_idle)
        .max_size(config.max_size)
        .idle_timeout(Duration::from_secs(config.idle_timeout))
        .build(manager)
        .await?;

    Ok(pool)
}

pub struct Redis(Pool<RedisConnectionManager>);

impl Service for Redis {
    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>());

        Redis(connection)
    }

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

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

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

        Ok(())
    }
}

impl Redis {
    pub async fn connection(&self) -> Result<PooledConnection<RedisConnectionManager>> {
        Ok(self.0.get().await?)
    }

    // https://docs.rs/bb8-redis/0.15.0/bb8_redis/
    pub async fn ping(&self) -> Result<()> {
        let mut conn = self.connection().await?;

        let reply: String = cmd("PING").query_async(&mut *conn).await?;

        if "PONG" != reply {
            return Err(eyre!("Redis ping failed"));
        }

        Ok(())
    }
}