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?)
}
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(())
}
}