use serde::{Deserialize, Serialize};
pub const ENV_CDK_MINTD_CACHE_BACKEND: &str = "CDK_MINTD_CACHE_BACKEND";
#[cfg(feature = "redis")]
pub const ENV_CDK_MINTD_CACHE_REDIS_URL: &str = "CDK_MINTD_CACHE_REDIS_URL";
#[cfg(feature = "redis")]
pub const ENV_CDK_MINTD_CACHE_REDIS_KEY_PREFIX: &str = "CDK_MINTD_CACHE_REDIS_KEY_PREFIX";
#[cfg(feature = "redis")]
pub const ENV_CDK_MINTD_CACHE_REDIS_USE_CLUSTER: &str = "CDK_MINTD_CACHE_REDIS_USE_CLUSTER";
#[cfg(feature = "redis")]
pub const ENV_CDK_MINTD_CACHE_REDIS_CLUSTER_NODES: &str = "CDK_MINTD_CACHE_REDIS_CLUSTER_NODES";
pub const ENV_CDK_MINTD_CACHE_TTI: &str = "CDK_MINTD_CACHE_TTI";
pub const ENV_CDK_MINTD_CACHE_TTL: &str = "CDK_MINTD_CACHE_TTL";
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(tag = "backend")]
#[serde(rename_all = "lowercase")]
pub enum Backend {
#[default]
Memory,
#[cfg(feature = "redis")]
Redis(super::backend::RedisConfig),
}
impl Backend {
pub fn from_env_str(backend_str: &str) -> Option<Self> {
match backend_str.to_lowercase().as_str() {
"memory" => Some(Self::Memory),
#[cfg(feature = "redis")]
"redis" => Some(Self::Redis(redis_config_from_env())),
_ => None,
}
}
}
#[cfg(feature = "redis")]
fn redis_config_from_env() -> super::backend::RedisConfig {
let use_cluster = std::env::var(ENV_CDK_MINTD_CACHE_REDIS_USE_CLUSTER)
.map(|v| v.to_lowercase() == "true")
.unwrap_or(false);
let connection_string = std::env::var(ENV_CDK_MINTD_CACHE_REDIS_URL)
.unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
let key_prefix = std::env::var(ENV_CDK_MINTD_CACHE_REDIS_KEY_PREFIX).ok();
let cluster_nodes = std::env::var(ENV_CDK_MINTD_CACHE_REDIS_CLUSTER_NODES)
.ok()
.map(|v| v.split(',').map(|s| s.trim().to_string()).collect());
super::backend::RedisConfig {
connection_string,
key_prefix,
use_cluster,
cluster_nodes,
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Config {
#[serde(default)]
#[serde(flatten)]
pub backend: Backend,
pub ttl: Option<u64>,
pub tti: Option<u64>,
}
impl Config {
pub fn from_env(mut self) -> Self {
use std::env;
if let Ok(backend_str) = env::var(ENV_CDK_MINTD_CACHE_BACKEND) {
if let Some(backend) = Backend::from_env_str(&backend_str) {
self.backend = backend;
#[cfg(feature = "redis")]
if matches!(self.backend, Backend::Redis(_)) {
self.backend = Backend::Redis(redis_config_from_env());
}
}
}
if let Ok(ttl_str) = env::var(ENV_CDK_MINTD_CACHE_TTL) {
if let Ok(ttl) = ttl_str.parse() {
self.ttl = Some(ttl);
}
}
if let Ok(tti_str) = env::var(ENV_CDK_MINTD_CACHE_TTI) {
if let Ok(tti) = tti_str.parse() {
self.tti = Some(tti);
}
}
self
}
}
#[cfg(all(test, feature = "redis"))]
mod tests {
use super::*;
#[test]
fn test_from_env_str_memory() {
assert!(matches!(
Backend::from_env_str("memory"),
Some(Backend::Memory)
));
assert!(matches!(
Backend::from_env_str("MEMORY"),
Some(Backend::Memory)
));
}
#[test]
fn test_from_env_str_unknown_returns_none() {
assert!(Backend::from_env_str("unknown").is_none());
assert!(Backend::from_env_str("").is_none());
}
#[test]
fn test_cluster_nodes_parsing() {
let raw = "redis://node1:6379, redis://node2:6379 , redis://node3:6379";
let parsed: Vec<String> = raw.split(',').map(|s| s.trim().to_string()).collect();
assert_eq!(parsed.len(), 3);
assert_eq!(parsed[0], "redis://node1:6379");
assert_eq!(parsed[1], "redis://node2:6379");
assert_eq!(parsed[2], "redis://node3:6379");
}
#[test]
fn test_use_cluster_flag_parsing() {
let truthy = "TRUE";
assert!(truthy.to_lowercase() == "true");
let falsy = "false";
assert!(!(falsy.to_lowercase() == "true"));
let empty = "";
assert!(!(empty.to_lowercase() == "true"));
}
#[test]
fn test_config_default() {
let config = Config::default();
assert!(matches!(config.backend, Backend::Memory));
assert!(config.ttl.is_none());
assert!(config.tti.is_none());
}
#[test]
fn test_redis_config_defaults() {
let cfg = super::super::backend::RedisConfig::default();
assert!(!cfg.use_cluster);
assert!(cfg.cluster_nodes.is_none());
assert_eq!(cfg.connection_string, "");
assert!(cfg.key_prefix.is_none());
}
}