sinan 0.1.0

A Boilerplate for Rapid Axum Web Service Deployment.
Documentation
use std::future::Future;

use bb8::{Pool, PooledConnection};
use bb8_redis::RedisConnectionManager;
use eyre::{OptionExt, Result};
use redis::{AsyncCommands, FromRedisValue, ToRedisArgs};
use serde::Serialize;
use serde_json::Value;

use crate::services::{Redis, service};

async fn store() -> Result<PooledConnection<'static,RedisConnectionManager>> {
    let client = service().get::<Redis>().unwrap();

    client.connection().await
}

pub async fn has<K>(key: K) -> eyre::Result<bool>
where
    K: ToRedisArgs + Send + Sync,
{
    let mut con = store().await?;

    let exist: bool = con.exists(key).await?;

    Ok(exist)
}

pub async fn get<K, T>(key: K) -> eyre::Result<T>
where
    K: ToRedisArgs + Send + Sync,
    T: FromRedisValue,
{
    let mut con = store().await?;

    let result: T = con.get(key).await?;

    tracing::debug!("cache hit"); // todo 记录 key

    Ok(result)
}

pub async fn set<K, V>(key: K, value: V, seconds: u64) -> eyre::Result<()>
where
    K: ToRedisArgs + Send + Sync,
    V: ToRedisArgs + Send + Sync,
{
    let mut con = store().await?;

    con.set_ex(key, value, seconds).await?;

    Ok(())
}

pub async fn get_json<K>(key: K) -> eyre::Result<Value>
where
    K: ToRedisArgs + Send + Sync,
{
    let value: String = get(key).await?;

    let result = serde_json::from_str(&value)?;

    Ok(result)
}

pub async fn set_json<K, V>(key: K, value: &V, seconds: u64) -> eyre::Result<()>
where
    K: ToRedisArgs + Send + Sync,
    V: Serialize,
{
    set(key, serde_json::to_string(value)?, seconds).await?;

    Ok(())
}

pub async fn remember<K, F, Fut>(key: K, seconds: u64, func: F) -> eyre::Result<Value>
where
    K: ToRedisArgs + Send + Sync,
    F: FnOnce() -> Fut,
    Fut: Future<Output = Result<Value>>,
{
    if has(&key).await? {
        return get_json(&key).await;
    }

    let value = func().await?;

    set_json(&key, &value, seconds).await?;

    Ok(value)
}

pub async fn remember_multi<K, F, Fut>(key: K, seconds: u64, func: F) -> eyre::Result<Vec<Value>>
where
    K: ToRedisArgs + Send + Sync,
    F: FnOnce() -> Fut,
    Fut: Future<Output = Result<Vec<Value>>>,
{
    if has(&key).await? {
        let result = get_json(&key).await?;

        let values = result.as_array().ok_or_eyre("not an array")?;

        return Ok(values.to_owned());
    }

    let values = func().await?;

    set_json(&key, &values, seconds).await?;

    Ok(values)
}