| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- 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,
- /// 自定义形象图片 URL(通过 /api/media/upload 上传后本地记录)
- #[serde(default)]
- pub custom_mascot_url: Option<String>,
- }
- impl Default for AppSettings {
- fn default() -> Self {
- Self { base_url: default_base_url(), custom_mascot_url: None }
- }
- }
- fn default_base_url() -> String {
- "https://cfc.cf-club.com".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, "https://cfc.cf-club.com");
- }
- #[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, "https://cfc.cf-club.com");
- }
- #[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);
- }
- #[test]
- fn custom_mascot_url_roundtrip() {
- let dir = temp_dir();
- let store = SettingsStore::new(dir.clone());
- let mut cfg = store.load();
- assert!(cfg.custom_mascot_url.is_none());
- cfg.custom_mascot_url = Some("https://cdn.example/x.png".into());
- store.save(&cfg).unwrap();
- let loaded = SettingsStore::new(dir.clone()).load();
- assert_eq!(loaded.custom_mascot_url.as_deref(), Some("https://cdn.example/x.png"));
- let _ = fs::remove_dir_all(&dir);
- }
- }
|