1use crate::store::CacheStore;
11use crate::{MemoryStore, NamespacedStore};
12use doido_core::Environment;
13use doido_core::Result;
14use serde::Deserialize;
15use std::sync::Arc;
16
17#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize)]
19#[serde(rename_all = "snake_case")]
20pub enum CacheBackend {
21 #[default]
23 Memory,
24 Redis,
26 Memcache,
28}
29
30#[derive(Debug, Clone, Default, Deserialize)]
39pub struct CacheConfig {
40 #[serde(default, rename = "type")]
42 pub backend: CacheBackend,
43 #[serde(default)]
46 pub endpoint: Option<String>,
47 #[serde(default)]
49 pub namespace: Option<String>,
50}
51
52impl CacheConfig {
53 pub async fn build(&self) -> Result<Arc<dyn CacheStore>> {
57 let base: Arc<dyn CacheStore> = match self.backend {
58 CacheBackend::Memory => Arc::new(MemoryStore::new()),
59 CacheBackend::Redis => self.build_redis().await?,
60 CacheBackend::Memcache => self.build_memcache().await?,
61 };
62 Ok(match &self.namespace {
63 Some(prefix) if !prefix.is_empty() => {
64 Arc::new(NamespacedStore::new(base, prefix.clone()))
65 }
66 _ => base,
67 })
68 }
69}
70
71#[derive(Debug, Clone, Default, Deserialize)]
80pub struct MultiCacheConfig {
81 #[serde(default)]
82 pub stores: std::collections::HashMap<String, CacheConfig>,
83}
84
85impl MultiCacheConfig {
86 pub async fn build_registry(&self) -> Result<crate::CacheRegistry> {
88 let mut registry = crate::CacheRegistry::new();
89 for (name, config) in &self.stores {
90 registry.add(name.clone(), config.build().await?);
91 }
92 Ok(registry)
93 }
94}
95
96impl CacheConfig {
97 #[cfg(feature = "cache-redis")]
98 async fn build_redis(&self) -> Result<Arc<dyn CacheStore>> {
99 let endpoint = self
100 .endpoint
101 .clone()
102 .unwrap_or_else(|| "redis://127.0.0.1:6379".to_string());
103 Ok(Arc::new(
104 crate::redis_store::RedisStore::connect(&endpoint).await?,
105 ))
106 }
107
108 #[cfg(not(feature = "cache-redis"))]
109 async fn build_redis(&self) -> Result<Arc<dyn CacheStore>> {
110 Err(doido_core::anyhow::anyhow!(
111 "cache backend 'redis' selected in config but doido-cache was built \
112 without the `cache-redis` feature"
113 ))
114 }
115
116 #[cfg(feature = "cache-memcache")]
117 async fn build_memcache(&self) -> Result<Arc<dyn CacheStore>> {
118 let endpoint = self
119 .endpoint
120 .clone()
121 .unwrap_or_else(|| "memcache://127.0.0.1:11211".to_string());
122 Ok(Arc::new(
123 crate::memcache_store::MemcacheStore::connect(endpoint).await?,
124 ))
125 }
126
127 #[cfg(not(feature = "cache-memcache"))]
128 async fn build_memcache(&self) -> Result<Arc<dyn CacheStore>> {
129 Err(doido_core::anyhow::anyhow!(
130 "cache backend 'memcache' selected in config but doido-cache was \
131 built without the `cache-memcache` feature"
132 ))
133 }
134}
135
136#[derive(Debug, Clone, Default, Deserialize)]
139pub struct YamlConfig {
140 #[serde(default)]
141 pub cache: CacheConfig,
142}
143
144impl YamlConfig {
145 pub fn load() -> std::io::Result<Self> {
147 Self::load_env(Environment::get_env())
148 }
149
150 pub fn load_env(env: Environment) -> std::io::Result<Self> {
152 let path = format!("config/{}.yml", env.as_str());
153 let contents = std::fs::read_to_string(&path)?;
154 Self::from_yaml(&contents)
155 }
156
157 pub fn from_yaml(yaml: &str) -> std::io::Result<Self> {
159 serde_norway::from_str(yaml)
160 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
161 }
162}
163
164pub fn load() -> CacheConfig {
167 YamlConfig::load().map(|c| c.cache).unwrap_or_default()
168}