Skip to main content

adk_auth/secrets/
cached.rs

1//! Cached secret provider wrapper.
2//!
3//! [`CachedSecretProvider`] wraps any [`SecretProvider`] with an in-memory
4//! cache that respects a configurable TTL.
5
6use std::collections::HashMap;
7use std::sync::Arc;
8use std::time::{Duration, Instant};
9
10use adk_core::AdkError;
11use async_trait::async_trait;
12use tokio::sync::RwLock;
13
14use super::provider::SecretProvider;
15
16/// A cached entry with expiration tracking.
17struct CachedEntry {
18    value: String,
19    expires_at: Instant,
20}
21
22/// Wraps a [`SecretProvider`] with an in-memory cache.
23///
24/// Cached values are returned within the configured TTL. After expiry,
25/// the inner provider is called again and the cache is refreshed.
26///
27/// # Example
28///
29/// ```rust,ignore
30/// use adk_auth::secrets::{CachedSecretProvider, SecretProvider};
31/// use std::time::Duration;
32///
33/// let cached = CachedSecretProvider::new(inner_provider, Duration::from_secs(300));
34/// let secret = cached.get_secret("my-key").await?;
35/// ```
36pub struct CachedSecretProvider<P: SecretProvider> {
37    inner: P,
38    cache: Arc<RwLock<HashMap<String, CachedEntry>>>,
39    ttl: Duration,
40}
41
42impl<P: SecretProvider> CachedSecretProvider<P> {
43    /// Create a new cached provider wrapping `inner` with the given TTL.
44    pub fn new(inner: P, ttl: Duration) -> Self {
45        Self { inner, cache: Arc::new(RwLock::new(HashMap::new())), ttl }
46    }
47}
48
49#[async_trait]
50impl<P: SecretProvider> SecretProvider for CachedSecretProvider<P> {
51    async fn get_secret(&self, name: &str) -> Result<String, AdkError> {
52        // Check cache first
53        {
54            let cache = self.cache.read().await;
55            if let Some(entry) = cache.get(name) {
56                if entry.expires_at > Instant::now() {
57                    return Ok(entry.value.clone());
58                }
59            }
60        }
61
62        // Cache miss or expired — fetch from inner provider
63        let value = self.inner.get_secret(name).await?;
64
65        // Update cache
66        {
67            let mut cache = self.cache.write().await;
68            cache.insert(
69                name.to_string(),
70                CachedEntry { value: value.clone(), expires_at: Instant::now() + self.ttl },
71            );
72        }
73
74        Ok(value)
75    }
76}