|
|
@@ -0,0 +1,91 @@
|
|
|
+use serde::{Deserialize, Serialize};
|
|
|
+use std::fs;
|
|
|
+use std::path::PathBuf;
|
|
|
+
|
|
|
+#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
+#[serde(rename_all = "camelCase")]
|
|
|
+pub struct AppSettings {
|
|
|
+ #[serde(default = "default_base_url")]
|
|
|
+ pub base_url: String,
|
|
|
+}
|
|
|
+
|
|
|
+impl Default for AppSettings {
|
|
|
+ fn default() -> Self {
|
|
|
+ Self { base_url: default_base_url() }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn default_base_url() -> String {
|
|
|
+ "http://localhost:9082".to_string()
|
|
|
+}
|
|
|
+
|
|
|
+pub struct SettingsStore {
|
|
|
+ path: PathBuf,
|
|
|
+}
|
|
|
+
|
|
|
+impl SettingsStore {
|
|
|
+ pub fn new(app_config_dir: PathBuf) -> Self {
|
|
|
+ Self { path: app_config_dir.join("settings.json") }
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn load(&self) -> AppSettings {
|
|
|
+ match fs::read_to_string(&self.path) {
|
|
|
+ Ok(txt) => serde_json::from_str(&txt).unwrap_or_default(),
|
|
|
+ Err(_) => AppSettings::default(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn save(&self, s: &AppSettings) -> Result<(), String> {
|
|
|
+ if let Some(dir) = self.path.parent() {
|
|
|
+ fs::create_dir_all(dir).map_err(|e| e.to_string())?;
|
|
|
+ }
|
|
|
+ let txt = serde_json::to_string_pretty(s).map_err(|e| e.to_string())?;
|
|
|
+ fs::write(&self.path, txt).map_err(|e| e.to_string())
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod tests {
|
|
|
+ use super::*;
|
|
|
+ use std::fs;
|
|
|
+ use std::sync::atomic::{AtomicUsize, Ordering};
|
|
|
+
|
|
|
+ static COUNTER: AtomicUsize = AtomicUsize::new(0);
|
|
|
+
|
|
|
+ fn temp_dir() -> std::path::PathBuf {
|
|
|
+ let n = COUNTER.fetch_add(1, Ordering::SeqCst);
|
|
|
+ let dir = std::env::temp_dir().join(format!("xafpet-settings-test-{}", n));
|
|
|
+ let _ = fs::remove_dir_all(&dir);
|
|
|
+ dir
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn default_when_file_missing() {
|
|
|
+ let store = SettingsStore::new(temp_dir());
|
|
|
+ let cfg = store.load();
|
|
|
+ assert_eq!(cfg.base_url, "http://localhost:9082");
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn default_when_file_malformed() {
|
|
|
+ let dir = temp_dir();
|
|
|
+ fs::create_dir_all(&dir).unwrap();
|
|
|
+ fs::write(dir.join("settings.json"), "not json").unwrap();
|
|
|
+ let store = SettingsStore::new(dir);
|
|
|
+ let cfg = store.load();
|
|
|
+ assert_eq!(cfg.base_url, "http://localhost:9082");
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn roundtrip_save_load() {
|
|
|
+ let dir = temp_dir();
|
|
|
+ let store = SettingsStore::new(dir.clone());
|
|
|
+ let mut cfg = store.load();
|
|
|
+ cfg.base_url = "http://192.168.1.5:9082".into();
|
|
|
+ store.save(&cfg).unwrap();
|
|
|
+
|
|
|
+ let store2 = SettingsStore::new(dir.clone());
|
|
|
+ assert_eq!(store2.load().base_url, "http://192.168.1.5:9082");
|
|
|
+ let _ = fs::remove_dir_all(&dir);
|
|
|
+ }
|
|
|
+}
|