use std::sync::RwLock;
use once_cell::sync::Lazy;
#[derive(Debug, Clone)]
pub struct RediqConfig {
pub max_payload_size: usize,
pub task_ttl: u64,
pub priority_range: (i32, i32),
pub default_max_retry: u32,
pub default_timeout_secs: u64,
}
impl Default for RediqConfig {
fn default() -> Self {
Self {
max_payload_size: 512 * 1024, task_ttl: 86400, priority_range: (0, 100),
default_max_retry: 3,
default_timeout_secs: 30,
}
}
}
impl RediqConfig {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_max_payload_size(mut self, size: usize) -> Self {
self.max_payload_size = size;
self
}
#[must_use]
pub fn with_task_ttl(mut self, ttl: u64) -> Self {
self.task_ttl = ttl;
self
}
#[must_use]
pub fn with_priority_range(mut self, min: i32, max: i32) -> Self {
self.priority_range = (min, max);
self
}
#[must_use]
pub fn with_default_max_retry(mut self, max_retry: u32) -> Self {
self.default_max_retry = max_retry;
self
}
#[must_use]
pub fn with_default_timeout(mut self, timeout_secs: u64) -> Self {
self.default_timeout_secs = timeout_secs;
self
}
pub fn validate_priority(&self, priority: i32) -> Result<(), String> {
if priority < self.priority_range.0 || priority > self.priority_range.1 {
Err(format!(
"Priority must be between {} and {}, got {}",
self.priority_range.0, self.priority_range.1, priority
))
} else {
Ok(())
}
}
pub fn clamp_priority(&self, priority: i32) -> i32 {
priority.clamp(self.priority_range.0, self.priority_range.1)
}
}
static GLOBAL_CONFIG: Lazy<RwLock<RediqConfig>> =
Lazy::new(|| RwLock::new(RediqConfig::default()));
pub fn get_config() -> RediqConfig {
GLOBAL_CONFIG
.read()
.map(|guard| guard.clone())
.unwrap_or_else(|e| {
tracing::error!("Failed to read global config (lock poisoned): {}. Using default configuration.", e);
RediqConfig::default()
})
}
pub fn set_config(config: RediqConfig) {
match GLOBAL_CONFIG.write() {
Ok(mut global) => {
*global = config;
tracing::info!("Global Rediq configuration updated");
}
Err(e) => {
tracing::error!("Failed to write global config (lock poisoned): {}. Configuration was not updated.", e);
}
}
}
pub fn update_config<F>(modifier: F)
where
F: FnOnce(&mut RediqConfig),
{
match GLOBAL_CONFIG.write() {
Ok(mut global) => {
modifier(&mut global);
tracing::info!("Global Rediq configuration updated");
}
Err(e) => {
tracing::error!("Failed to update global config (lock poisoned): {}. Configuration was not updated.", e);
}
}
}
pub fn get_max_payload_size() -> usize {
get_config().max_payload_size
}
pub fn get_task_ttl() -> u64 {
get_config().task_ttl
}
pub fn get_priority_range() -> (i32, i32) {
get_config().priority_range
}
pub fn get_default_max_retry() -> u32 {
get_config().default_max_retry
}
pub fn get_default_timeout_secs() -> u64 {
get_config().default_timeout_secs
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
static TEST_LOCK: Mutex<()> = Mutex::new(());
#[test]
fn test_default_config() {
let config = RediqConfig::default();
assert_eq!(config.max_payload_size, 512 * 1024);
assert_eq!(config.task_ttl, 86400);
assert_eq!(config.priority_range, (0, 100));
assert_eq!(config.default_max_retry, 3);
assert_eq!(config.default_timeout_secs, 30);
}
#[test]
fn test_config_builder() {
let config = RediqConfig::new()
.with_max_payload_size(1024 * 1024)
.with_task_ttl(3600)
.with_priority_range(1, 10)
.with_default_max_retry(5)
.with_default_timeout(60);
assert_eq!(config.max_payload_size, 1024 * 1024);
assert_eq!(config.task_ttl, 3600);
assert_eq!(config.priority_range, (1, 10));
assert_eq!(config.default_max_retry, 5);
assert_eq!(config.default_timeout_secs, 60);
}
#[test]
fn test_validate_priority() {
let config = RediqConfig::new().with_priority_range(0, 100);
assert!(config.validate_priority(50).is_ok());
assert!(config.validate_priority(0).is_ok());
assert!(config.validate_priority(100).is_ok());
assert!(config.validate_priority(-1).is_err());
assert!(config.validate_priority(101).is_err());
}
#[test]
fn test_clamp_priority() {
let config = RediqConfig::new().with_priority_range(10, 90);
assert_eq!(config.clamp_priority(50), 50);
assert_eq!(config.clamp_priority(5), 10);
assert_eq!(config.clamp_priority(95), 90);
}
#[test]
fn test_global_config() {
let _lock = TEST_LOCK.lock().unwrap();
let original = get_config();
let new_config = RediqConfig::new()
.with_max_payload_size(2048);
set_config(new_config.clone());
assert_eq!(get_config().max_payload_size, 2048);
update_config(|c| {
c.task_ttl = 7200;
});
assert_eq!(get_config().task_ttl, 7200);
assert_eq!(get_config().max_payload_size, 2048);
set_config(original);
}
#[test]
fn test_config_getters() {
let _lock = TEST_LOCK.lock().unwrap();
let original = get_config();
set_config(RediqConfig::new()
.with_max_payload_size(123456)
.with_task_ttl(12345)
.with_priority_range(5, 95)
.with_default_max_retry(7)
.with_default_timeout(45));
assert_eq!(get_max_payload_size(), 123456);
assert_eq!(get_task_ttl(), 12345);
assert_eq!(get_priority_range(), (5, 95));
assert_eq!(get_default_max_retry(), 7);
assert_eq!(get_default_timeout_secs(), 45);
set_config(original);
}
}