use std::collections::HashMap;
use std::path::PathBuf;
use serde_json::Value;
use super::SettingsError;
#[derive(Debug)]
pub struct SettingsStore {
path: PathBuf,
map: HashMap<String, Value>,
}
impl SettingsStore {
pub async fn open(
root: impl AsRef<std::path::Path>,
namespace: &str,
) -> Result<Self, SettingsError> {
let path = root.as_ref().join(format!("{namespace}.json"));
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)
.map_err(|e| SettingsError::Io(e.to_string()))?;
}
let map = if path.exists() {
let content = std::fs::read_to_string(&path)
.map_err(|e| SettingsError::Io(e.to_string()))?;
serde_json::from_str::<HashMap<String, Value>>(&content)
.map_err(|e| SettingsError::Serde(e.to_string()))?
} else {
HashMap::new()
};
Ok(Self { path, map })
}
pub fn get<T: serde::de::DeserializeOwned>(&self, key: &str) -> Option<T> {
let value = self.map.get(key)?;
serde_json::from_value(value.clone()).ok()
}
pub fn set<T: serde::Serialize>(
&mut self,
key: &str,
value: &T,
) -> Result<(), SettingsError> {
let json_value =
serde_json::to_value(value).map_err(|e| SettingsError::Serde(e.to_string()))?;
self.map.insert(key.to_string(), json_value);
Ok(())
}
pub fn remove(&mut self, key: &str) -> bool {
self.map.remove(key).is_some()
}
pub fn keys(&self) -> Vec<String> {
self.map.keys().cloned().collect()
}
pub fn contains(&self, key: &str) -> bool {
self.map.contains_key(key)
}
pub async fn save(&self) -> Result<(), SettingsError> {
let json = serde_json::to_string_pretty(&self.map)
.map_err(|e| SettingsError::Serde(e.to_string()))?;
let tmp_path = {
let mut p = self.path.clone();
let fname = p
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("settings")
.to_string();
p.set_file_name(format!("{fname}.tmp"));
p
};
std::fs::write(&tmp_path, &json)
.map_err(|e| SettingsError::Io(e.to_string()))?;
std::fs::rename(&tmp_path, &self.path)
.map_err(|e| SettingsError::Io(e.to_string()))?;
Ok(())
}
}