Attribute Macro cache

Source
#[cache]
Expand description

#[cache] - Transparent Redis-based caching for async functions.

This macro wraps an async function to automatically cache its result in Redis. It checks for a cached value before executing the function. If a cached result is found, it is deserialized and returned directly. Otherwise, the function runs normally and its result is stored in Redis.

§Syntax

#[cache("key_pattern", expire = <seconds>, condition = <bool_expr>, unless = <bool_expr>)]

§Attributes

  • "key_pattern" (required): A format string used to generate the cache key. Function arguments can be interpolated using standard format! syntax.
  • expire = <integer> (optional): The number of seconds before the cached value expires. If omitted, the key will be stored without expiration.
  • condition = <expression> (optional): A boolean expression evaluated before executing the function. If this evaluates to false, caching is completely bypassed — no lookup and no insertion. The expression can access function parameters directly.
  • unless = <expression> (optional): A boolean expression evaluated after executing the function. If this evaluates to true, the result will not be written to the cache. The expression can access both parameters and a result variable (the return value). NOTE: If your function returns Result<T, E>, the result variable in unless refers to the inner Ok value (T), not the entire Result. This allows you to write expressions like result.is_none() for Result<Option<_>, _> functions.

§Function Requirements

  • Must be an async fn
  • Can return either a Result<T, E> or a plain value T
  • The return type must implement serde::Serialize and serde::Deserialize
  • Generics, attributes, and visibility will be preserved

§Example

use spring_macros::cache;

#[derive(serde::Serialize, serde::Deserialize)]
struct User {
    id: u64,
    name: String,
}

struct MyError;

#[cache("user:{user_id}", expire = 600, condition = user_id % 2 == 0, unless = result.is_none())]
async fn get_user(user_id: u64) -> Result<Option<User>, MyError> {
    // Fetch user from database
    unimplemented!("do something")
}