1use std::path::PathBuf;
10
11use serde::{Deserialize, Serialize};
12
13use crate::model::{Exposure, Starter};
14
15#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
18pub struct GlobalConfig {
19 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub default_port: Option<u16>,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
24 pub default_expose: Option<Exposure>,
25 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub autoserve: Option<bool>,
28 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub idle_timeout_secs: Option<u64>,
31 #[serde(default, skip_serializing_if = "Option::is_none")]
33 pub starter: Option<Starter>,
34 #[serde(default, skip_serializing_if = "Option::is_none")]
36 pub skill_target: Option<String>,
37 #[serde(default, skip_serializing_if = "Option::is_none")]
39 pub skill_global: Option<bool>,
40 #[serde(default, skip_serializing_if = "Option::is_none")]
42 pub ui_accent: Option<String>,
43 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub ui_theme: Option<String>,
46}
47
48impl GlobalConfig {
49 pub fn path() -> Option<PathBuf> {
52 if let Ok(dir) = std::env::var("WIPE_CONFIG_DIR") {
53 if !dir.trim().is_empty() {
54 return Some(PathBuf::from(dir).join("config.json"));
55 }
56 }
57 directories::ProjectDirs::from("dev", "wipe", "wipe")
58 .map(|d| d.config_dir().join("config.json"))
59 }
60
61 pub fn load() -> Self {
63 Self::path()
64 .and_then(|p| std::fs::read(p).ok())
65 .and_then(|b| serde_json::from_slice(&b).ok())
66 .unwrap_or_default()
67 }
68
69 pub fn save(&self) -> std::io::Result<()> {
72 if let Some(path) = Self::path() {
73 if let Some(dir) = path.parent() {
74 std::fs::create_dir_all(dir)?;
75 }
76 let mut s = serde_json::to_string_pretty(self).unwrap_or_default();
77 s.push('\n');
78 std::fs::write(path, s)?;
79 }
80 Ok(())
81 }
82}