use std::time::Duration; use ::cache::Cache; use serde::Serialize; use serde::de::DeserializeOwned; use crate::share::errors::GitError; pub const REV_TTL: Duration = Duration::from_secs(60); pub const ANALYSIS_TTL: Duration = Duration::from_secs(600); pub const IMMUTABLE_TTL: Duration = Duration::from_secs(30 * 24 * 3600); pub fn sanitize_key(k: &str) -> String { let mut out = String::with_capacity(k.len()); for c in k.chars() { if c.is_ascii_alphanumeric() || matches!(c, '-' | '_' | '.' | ':' | '/') { out.push(c); } else { out.push('_'); } } out } pub async fn cached_fetch( cache: &Cache, key: &str, ttl: Duration, fetch: F, ) -> Result where V: Serialize + DeserializeOwned, F: FnOnce() -> Fut, Fut: std::future::Future>, { let sanitized = sanitize_key(key); if let Ok(Some(cached)) = cache.get_json::(&sanitized).await { return Ok(cached); } let val = fetch().await?; let _ = cache.set_json_ex(&sanitized, &val, ttl).await; Ok(val) } fn oid_key(kind: &str, oid: &str) -> String { sanitize_key(&format!("object.{kind}.{oid}")) } fn rev_key(module: &str, func: &str, key_part: &str, tip: &str) -> String { sanitize_key(&format!("{module}.{func}.{key_part}:{tip}")) } pub async fn cache_oid_get(cache: &Cache, kind: &str, oid: &str) -> Result, GitError> where V: DeserializeOwned, { Ok(cache.get_json(&oid_key(kind, oid)).await?) } pub async fn cache_oid_put(cache: &Cache, kind: &str, oid: &str, v: &V) -> Result<(), GitError> where V: Serialize, { cache.set_json(&oid_key(kind, oid), v).await?; Ok(()) } pub async fn cache_rev_get( cache: &Cache, module: &str, func: &str, key_part: &str, tip: &str, ) -> Result, GitError> where V: DeserializeOwned, { Ok(cache .get_json(&rev_key(module, func, key_part, tip)) .await?) } pub async fn cache_rev_put( cache: &Cache, module: &str, func: &str, key_part: &str, tip: &str, v: &V, ) -> Result<(), GitError> where V: Serialize, { cache .set_json_ex(&rev_key(module, func, key_part, tip), v, REV_TTL) .await?; Ok(()) }