1#[cfg(feature = "cache-memory")]
6pub mod memory;
7
8#[cfg(feature = "cache-redis")]
9pub mod redis;
10
11use std::time::Duration;
12
13#[async_trait::async_trait]
15pub trait Cache: Send + Sync {
16 async fn get(&self, key: &str) -> Option<String>;
18
19 async fn set(
25 &self,
26 key: String,
27 value: String,
28 ttl: Option<Duration>,
29 ) -> crate::error::Result<()>;
30
31 async fn delete(&self, key: &str) -> crate::error::Result<()>;
37
38 async fn clear(&self) -> crate::error::Result<()>;
44
45 async fn exists(&self, key: &str) -> bool;
47}
48
49#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
51pub struct CacheConfig {
52 pub cache_type: String,
54
55 pub memory_size: Option<usize>,
57
58 pub redis_url: Option<String>,
60
61 #[serde(default = "default_key_prefix")]
63 pub key_prefix: String,
64
65 pub default_ttl: Option<u64>,
67}
68
69#[must_use]
71pub fn default_key_prefix() -> String {
72 String::new()
73}
74
75impl Default for CacheConfig {
76 fn default() -> Self {
77 Self {
78 cache_type: "memory".to_string(),
79 memory_size: Some(1000),
80 redis_url: None,
81 key_prefix: String::new(),
82 default_ttl: Some(3600), }
84 }
85}
86
87pub fn create_cache(config: &CacheConfig) -> Result<Box<dyn Cache>, crate::error::Error> {
93 match config.cache_type.as_str() {
94 "memory" => {
95 #[cfg(feature = "cache-memory")]
96 {
97 let size = config.memory_size.unwrap_or(1000);
98 Ok(Box::new(memory::MemoryCache::new(size)))
99 }
100 #[cfg(not(feature = "cache-memory"))]
101 {
102 Err(crate::error::Error::Config(
103 "memory cache feature is not enabled".to_string(),
104 ))
105 }
106 }
107 "redis" => {
108 #[cfg(feature = "cache-redis")]
109 {
110 Err(crate::error::Error::Config(
113 "Redis cache requires async initialization. Use create_cache_async instead."
114 .to_string(),
115 ))
116 }
117 #[cfg(not(feature = "cache-redis"))]
118 {
119 Err(crate::error::Error::Config(
120 "redis cache feature is not enabled".to_string(),
121 ))
122 }
123 }
124 _ => Err(crate::error::Error::Config(format!(
125 "unsupported cache type: {}",
126 config.cache_type
127 ))),
128 }
129}
130
131#[cfg(feature = "cache-redis")]
137pub async fn create_cache_async(
138 config: &CacheConfig,
139) -> Result<Box<dyn Cache>, crate::error::Error> {
140 match config.cache_type.as_str() {
141 "memory" => {
142 let size = config.memory_size.unwrap_or(1000);
143 Ok(Box::new(memory::MemoryCache::new(size)))
144 }
145 "redis" => {
146 let url = config
147 .redis_url
148 .as_ref()
149 .ok_or_else(|| crate::error::Error::Config("redis_url is required".to_string()))?;
150 Ok(Box::new(
151 redis::RedisCache::new(url, config.key_prefix.clone()).await?,
152 ))
153 }
154 _ => Err(crate::error::Error::Config(format!(
155 "unsupported cache type: {}",
156 config.cache_type
157 ))),
158 }
159}