use crate::types::AppSettings;
use std::fs;
use std::path::PathBuf;
use std::sync::{OnceLock, RwLock};
static SETTINGS: OnceLock<RwLock<AppSettings>> = OnceLock::new();
#[derive(Debug, Clone, Copy)]
pub struct Settings {
pub settings: AppSettings,
}
impl Settings {
pub fn get_settings() -> Self {
let lock = Self::get_settings_lock();
let read_guard = lock.read().expect("Failed to acquire settings read lock");
Self {
settings: *read_guard,
}
}
pub fn get_settings_lock() -> &'static RwLock<AppSettings> {
SETTINGS.get_or_init(|| {
let settings = Self::load_settings_from_disk().unwrap_or_else(|_| {
let defaults = AppSettings::default();
let _ = Self::save_settings_to_disk(&defaults);
defaults
});
RwLock::new(settings)
})
}
#[allow(dead_code)]
fn get_home_dir() -> Option<PathBuf> {
#[cfg(target_os = "windows")]
{
std::env::var("USERPROFILE")
.ok()
.or_else(|| {
let drive = std::env::var("HOMEDRIVE").ok()?;
let path = std::env::var("HOMEPATH").ok()?;
Some(format!("{}{}", drive, path))
})
.map(PathBuf::from)
}
#[cfg(not(target_os = "windows"))]
{
std::env::var("HOME").ok().map(PathBuf::from)
}
}
fn get_settings_path() -> Option<PathBuf> {
let is_test = cfg!(test)
|| std::env::current_exe()
.map(|p| {
let s = p.to_string_lossy();
s.contains("/deps/") || s.contains("\\deps\\")
})
.unwrap_or(false);
if is_test {
let mut path = std::env::temp_dir();
path.push(format!(
"jsonette_test_settings_{}.json",
std::process::id()
));
Some(path)
} else {
#[cfg(target_os = "windows")]
{
let mut path = std::env::var("LOCALAPPDATA")
.ok()
.map(PathBuf::from)
.or_else(|| {
let mut home = Self::get_home_dir()?;
home.push("AppData");
home.push("Local");
Some(home)
})?;
path.push("jsonette");
path.push("settings.json");
Some(path)
}
#[cfg(not(target_os = "windows"))]
{
let mut path = std::env::var("XDG_CONFIG_HOME")
.ok()
.map(PathBuf::from)
.or_else(|| {
let mut home = Self::get_home_dir()?;
home.push(".config");
Some(home)
})?;
path.push("jsonette");
path.push("settings.json");
Some(path)
}
}
}
fn load_settings_from_disk() -> Result<AppSettings, String> {
let path = Self::get_settings_path()
.ok_or_else(|| "Unable to locate home directory".to_string())?;
if !path.exists() {
let defaults = AppSettings::default();
Self::save_settings_to_disk(&defaults)?;
return Ok(defaults);
}
let content = fs::read_to_string(&path)
.map_err(|e| format!("Failed to read settings file: {}", e))?;
let settings: AppSettings = serde_json::from_str(&content)
.map_err(|e| format!("Failed to parse settings JSON: {}", e))?;
Ok(settings)
}
fn save_settings_to_disk(settings: &AppSettings) -> Result<(), String> {
let path = Self::get_settings_path()
.ok_or_else(|| "Unable to locate home directory".to_string())?;
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
.map_err(|e| format!("Failed to create settings directory: {}", e))?;
}
let content = serde_json::to_string_pretty(settings)
.map_err(|e| format!("Failed to serialize settings: {}", e))?;
fs::write(&path, content).map_err(|e| format!("Failed to write settings file: {}", e))?;
Ok(())
}
}
pub fn get_settings() -> AppSettings {
Settings::get_settings().settings
}
pub fn update_settings(settings: AppSettings) -> Result<(), String> {
let lock = Settings::get_settings_lock();
{
let mut write_guard = lock.write().expect("Failed to acquire settings write lock");
*write_guard = settings;
}
Settings::save_settings_to_disk(&settings)
}
pub fn set_in_memory_settings(settings: AppSettings) {
let lock = Settings::get_settings_lock();
let mut write_guard = lock.write().expect("Failed to acquire settings write lock");
*write_guard = settings;
}