use crate::error::{Error, Result};
use crate::utils::sync::RwLockExt;
use serde_json::Value;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
pub struct CachedSettings {
pub stored: Value,
pub merged: std::sync::OnceLock<Value>,
pub defaults: Arc<HashMap<String, Value>>,
pub generation: u64,
}
pub struct SettingsCache {
state: RwLock<Option<CachedSettings>>,
}
impl SettingsCache {
pub fn new() -> Self {
Self {
state: RwLock::new(None),
}
}
pub fn invalidate(&self) {
if let Ok(mut guard) = self.state.write_recovered() {
*guard = None;
}
}
pub fn get_value(
&self,
category: &str,
setting_name: &str,
key: &str,
) -> Result<Option<Value>> {
let guard = self.state.read_recovered()?;
if let Some(cached) = guard.as_ref() {
if let Some(value) = cached
.stored
.get(category)
.and_then(|cat| cat.get(setting_name))
{
return Ok(Some(value.clone()));
}
if let Some(value) = cached.defaults.get(key) {
return Ok(Some(value.clone()));
}
}
Ok(None)
}
pub fn get_or_compute_merged<F>(&self, computer: F) -> Result<Value>
where
F: Fn(&Value) -> Result<Value>,
{
let guard = self.state.read_recovered()?;
if let Some(cached) = guard.as_ref() {
if let Some(merged) = cached.merged.get() {
return Ok(merged.clone());
}
let computed_val = computer(&cached.stored)?;
let _ = cached.merged.set(computed_val);
return cached
.merged
.get()
.cloned()
.ok_or_else(|| Error::Config("Merged cache initialization failed".into()));
}
Err(Error::Config("Cache not populated".into()))
}
pub fn get_stored(&self) -> Result<Option<Value>> {
let guard = self.state.read_recovered()?;
Ok(guard.as_ref().map(|c| c.stored.clone()))
}
pub fn is_populated(&self) -> bool {
self.state
.read_recovered()
.map(|g| g.is_some())
.unwrap_or(false)
}
pub fn populate<F>(&self, factory: F) -> Result<()>
where
F: FnOnce() -> Result<CachedSettings>,
{
if self.is_populated() {
return Ok(());
}
let mut guard = self.state.write_recovered()?;
if guard.is_some() {
return Ok(());
}
*guard = Some(factory()?);
Ok(())
}
pub fn update_stored(&self, new_stored: Value) -> Result<()> {
let mut guard = self.state.write_recovered()?;
if let Some(ref mut cached) = *guard {
cached.stored = new_stored;
cached.merged = std::sync::OnceLock::new(); cached.generation += 1;
}
Ok(())
}
}