oracle_lib/config/
settings.rs1use crate::error::Result;
4use serde::{Deserialize, Serialize};
5use std::path::PathBuf;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Settings {
9 pub ui: UiSettings,
10 pub analyzer: AnalyzerSettings,
11 pub keybindings: KeybindingSettings,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct UiSettings {
16 pub theme: String,
17 pub show_line_numbers: bool,
18 pub vim_mode: bool,
19 pub tab_width: usize,
20 pub wrap_text: bool,
21 pub accent_color: String,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct AnalyzerSettings {
26 pub include_private: bool,
27 pub include_tests: bool,
28 pub max_depth: usize,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct KeybindingSettings {
33 pub quit: String,
34 pub search: String,
35 pub help: String,
36 pub next_tab: String,
37 pub prev_tab: String,
38 pub select: String,
39}
40
41impl Default for Settings {
42 fn default() -> Self {
43 Self {
44 ui: UiSettings {
45 theme: "default".into(),
46 show_line_numbers: true,
47 vim_mode: false,
48 tab_width: 4,
49 wrap_text: false,
50 accent_color: "#4EBF71".into(),
51 },
52 analyzer: AnalyzerSettings {
53 include_private: true,
54 include_tests: false,
55 max_depth: 10,
56 },
57 keybindings: KeybindingSettings {
58 quit: "q".into(),
59 search: "/".into(),
60 help: "?".into(),
61 next_tab: "Tab".into(),
62 prev_tab: "Shift+Tab".into(),
63 select: "Enter".into(),
64 },
65 }
66 }
67}
68
69impl Settings {
70 pub fn load() -> Result<Self> {
71 let config_path = Self::config_path()?;
72
73 if !config_path.exists() {
74 return Ok(Self::default());
75 }
76
77 let content = std::fs::read_to_string(&config_path)?;
78 let settings: Settings = serde_yaml::from_str(&content)?;
79 Ok(settings)
80 }
81
82 pub fn save(&self) -> Result<()> {
83 let config_path = Self::config_path()?;
84
85 if let Some(parent) = config_path.parent() {
86 std::fs::create_dir_all(parent)?;
87 }
88
89 let content = serde_yaml::to_string(self)?;
90 std::fs::write(&config_path, content)?;
91 Ok(())
92 }
93
94 fn config_path() -> Result<PathBuf> {
95 let config_dir = dirs::config_dir()
96 .ok_or_else(|| crate::error::OracleError::Config("No config directory".into()))?;
97 Ok(config_dir.join("oracle").join("config.yaml"))
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn test_settings_default() {
107 let s = Settings::default();
108 assert_eq!(s.ui.theme, "default");
109 assert!(s.ui.show_line_numbers);
110 assert_eq!(s.keybindings.quit, "q");
111 assert_eq!(s.keybindings.search, "/");
112 assert!(s.analyzer.include_private);
113 assert_eq!(s.analyzer.max_depth, 10);
114 }
115
116 #[test]
117 fn test_settings_roundtrip_yaml() {
118 let s = Settings::default();
119 let yaml = serde_yaml::to_string(&s).unwrap();
120 let loaded: Settings = serde_yaml::from_str(&yaml).unwrap();
121 assert_eq!(s.ui.theme, loaded.ui.theme);
122 assert_eq!(s.keybindings.quit, loaded.keybindings.quit);
123 }
124}