use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub mod builder;
#[cfg(feature = "l1-moka")]
pub mod layer;
pub mod legacy_config;
pub mod service;
pub mod validation;
#[cfg(feature = "confers")]
pub mod confers_macro;
#[cfg(feature = "config-dynamic")]
pub mod dynamic;
#[cfg(feature = "confers")]
pub use confers_macro::confers_load as load_from_file;
#[cfg(feature = "l2-redis")]
pub use crate::config::legacy_config::{ClusterConfig, SentinelConfig};
pub use builder::OxcacheConfigBuilder;
#[cfg(feature = "l1-moka")]
pub use layer::{EvictionPolicy, L1LayerConfig, L2LayerConfig, LayerConfig, TwoLevelLayerConfig};
#[cfg(feature = "bloom-filter")]
pub use service::BloomFilterConfig;
#[cfg(feature = "l1-moka")]
pub use service::L1Config;
pub use service::{CacheType, RedisMode, ServiceConfig};
#[cfg(feature = "l2-redis")]
pub use service::{L2Config, TwoLevelConfig};
pub use validation::ConfigValidation;
pub use self::legacy_config::{
CacheStrategy, CacheWarmupConfig, Config as LegacyConfig, DynamicConfig,
EvictionPolicy as LegacyEvictionPolicy, GlobalConfig as LegacyGlobalConfig,
InvalidationChannelConfig, RedisMode as LegacyRedisMode, SerializationType, WarmupDataSource,
};
#[cfg(feature = "confers")]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub enum ConfigSource {
Code,
Macro(String),
File(String),
}
pub const CONFIG_VERSION: u32 = 2;
pub const CONFIG_VERSION_FIELD: &str = "config_version";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlobalConfig {
pub default_ttl: u64,
pub health_check_interval: u64,
pub serialization: SerializationType,
pub enable_metrics: bool,
}
impl Default for GlobalConfig {
fn default() -> Self {
Self {
default_ttl: 300,
health_check_interval: 60,
serialization: SerializationType::default(),
enable_metrics: false,
}
}
}
impl GlobalConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_default_ttl(mut self, ttl: u64) -> Self {
self.default_ttl = ttl;
self
}
pub fn with_health_check_interval(mut self, interval: u64) -> Self {
self.health_check_interval = interval;
self
}
pub fn with_serialization(mut self, serialization: SerializationType) -> Self {
self.serialization = serialization;
self
}
pub fn with_enable_metrics(mut self, enable: bool) -> Self {
self.enable_metrics = enable;
self
}
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct OxcacheConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub config_version: Option<u32>,
pub global: GlobalConfig,
pub services: HashMap<String, ServiceConfig>,
#[cfg(feature = "l1-moka")]
pub layer: Option<LayerConfig>,
#[cfg(feature = "confers")]
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub extensions: HashMap<String, serde_json::Value>,
#[cfg(feature = "confers")]
pub source: Option<ConfigSource>,
}
impl OxcacheConfig {
pub fn new() -> Self {
Self::default()
}
pub fn builder() -> OxcacheConfigBuilder {
OxcacheConfigBuilder::new()
}
pub fn validate(&self) -> Result<(), String> {
ConfigValidation::validate(self)
}
#[cfg(feature = "confers")]
pub fn source(&self) -> &Option<ConfigSource> {
&self.source
}
#[cfg(feature = "confers")]
pub fn set_source(&mut self, source: ConfigSource) {
self.source = Some(source);
}
pub fn is_l1_enabled(&self) -> bool {
cfg!(feature = "l1-moka")
}
pub fn is_l2_enabled(&self) -> bool {
cfg!(feature = "l2-redis")
}
pub fn available_features(&self) -> Vec<&'static str> {
let mut features = Vec::new();
add_feature_if_enabled!(features, "l1-moka");
add_feature_if_enabled!(features, "l2-redis");
add_feature_if_enabled!(features, "bloom-filter");
add_feature_if_enabled!(features, "rate-limiting");
add_feature_if_enabled!(features, "batch-write");
add_feature_if_enabled!(features, "wal-recovery");
add_feature_if_enabled!(features, "serialization");
add_feature_if_enabled!(features, "compression");
add_feature_if_enabled!(features, "database");
add_feature_if_enabled!(features, "cli");
add_feature_if_enabled!(features, "opentelemetry");
add_feature_if_enabled!(features, "metrics");
add_feature_if_enabled!(features, "confers");
features
}
}
pub fn oxcache_config() -> OxcacheConfigBuilder {
OxcacheConfigBuilder::new()
}
#[cfg(feature = "confers")]
pub fn load_config_from_file(path: &str) -> Result<OxcacheConfig, String> {
confers_macro::confers_load(path)
}
#[deprecated(since = "0.2.0", note = "请使用 `OxcacheConfig` 替代 `Config`")]
pub type Config = OxcacheConfig;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_oxcache_config_default() {
let config = OxcacheConfig::default();
assert!(config.services.is_empty());
#[cfg(feature = "l1-moka")]
assert!(config.layer.is_none());
}
#[test]
fn test_oxcache_config_builder() {
let config = oxcache_config()
.with_global(GlobalConfig::default())
.build();
assert!(!config.services.is_empty() || config.global.default_ttl == 300);
}
#[test]
fn test_global_config_builder() {
let global = GlobalConfig::new()
.with_default_ttl(600)
.with_health_check_interval(30)
.with_serialization(SerializationType::Json)
.with_enable_metrics(true);
assert_eq!(global.default_ttl, 600);
}
#[test]
fn test_feature_flags() {
let config = OxcacheConfig::new();
#[cfg(feature = "l1-moka")]
assert!(config.is_l1_enabled());
#[cfg(feature = "l2-redis")]
assert!(config.is_l2_enabled());
}
}