use std::any::{Any, TypeId};
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::sync::RwLock;
use crate::core::config::{ConfigHandle, ConfigKey};
use crate::kit::error::KitError;
pub struct ConfigStore {
inner: RwLock<HashMap<TypeId, Box<dyn Any + Send + Sync>>>,
}
impl ConfigStore {
pub fn new() -> Self {
ConfigStore {
inner: RwLock::new(HashMap::new()),
}
}
pub fn set_config<K>(&self, config: K::Config)
where
K: ConfigKey,
{
let mut store = self.inner.write().expect("ConfigStore write lock poisoned");
let key = TypeId::of::<K>();
match store.entry(key) {
Entry::Occupied(e) => {
let handle = e
.get()
.downcast_ref::<ConfigHandle<K::Config>>()
.expect("type mismatch in config store");
handle.set(config);
}
Entry::Vacant(e) => {
e.insert(Box::new(ConfigHandle::new(config)));
}
}
}
pub fn config<K>(&self) -> Result<ConfigHandle<K::Config>, KitError>
where
K: ConfigKey,
{
let store = self.inner.read().expect("ConfigStore read lock poisoned");
let key = TypeId::of::<K>();
let boxed = store
.get(&key)
.ok_or(KitError::MissingConfig { key: K::NAME })?;
let handle = boxed
.downcast_ref::<ConfigHandle<K::Config>>()
.expect("type mismatch in config store");
Ok(handle.clone())
}
pub fn contains_config<K>(&self) -> bool
where
K: ConfigKey,
{
let store = self.inner.read().expect("ConfigStore read lock poisoned");
store.contains_key(&TypeId::of::<K>())
}
}
impl Default for ConfigStore {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::config::ConfigKey;
struct TestConfig;
impl ConfigKey for TestConfig {
type Config = i32;
const NAME: &'static str = "test_config";
}
#[test]
fn default_creates_empty_store() {
let store = ConfigStore::default();
let inner = store.inner.read().expect("ConfigStore read lock poisoned");
assert!(inner.is_empty(), "default ConfigStore should be empty");
}
#[test]
fn set_config_stores_handle() {
let store = ConfigStore::default();
assert!(!store.contains_config::<TestConfig>());
store.set_config::<TestConfig>(42);
assert!(store.contains_config::<TestConfig>());
}
#[test]
fn set_config_replaces_existing() {
let store = ConfigStore::default();
store.set_config::<TestConfig>(1);
store.set_config::<TestConfig>(2);
let handle = store.config::<TestConfig>().unwrap();
assert_eq!(*handle.load(), 2);
}
#[test]
fn config_returns_stored_handle() {
let store = ConfigStore::default();
store.set_config::<TestConfig>(100);
let handle = store.config::<TestConfig>().unwrap();
assert_eq!(*handle.load(), 100);
}
#[test]
fn config_missing_returns_error() {
let store = ConfigStore::default();
let result = store.config::<TestConfig>();
assert!(result.is_err());
assert!(matches!(
result,
Err(KitError::MissingConfig { key: "test_config" })
));
}
}