omni_dev/utils/
settings.rs1use std::collections::HashMap;
7use std::env;
8use std::fs;
9use std::path::{Path, PathBuf};
10
11use anyhow::{Context, Result};
12use serde::Deserialize;
13
14#[derive(Debug, Deserialize)]
16pub struct Settings {
17 #[serde(default)]
19 pub env: HashMap<String, String>,
20}
21
22impl Settings {
23 pub fn load() -> Result<Self> {
25 let settings_path = Self::get_settings_path()?;
26 Self::load_from_path(&settings_path)
27 }
28
29 pub fn load_from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
31 let path = path.as_ref();
32
33 if !path.exists() {
35 return Ok(Self {
36 env: HashMap::new(),
37 });
38 }
39
40 let content = fs::read_to_string(path)
42 .with_context(|| format!("Failed to read settings file: {}", path.display()))?;
43
44 serde_json::from_str::<Self>(&content)
45 .with_context(|| format!("Failed to parse settings file: {}", path.display()))
46 }
47
48 pub fn get_settings_path() -> Result<PathBuf> {
50 let home_dir = dirs::home_dir().context("Failed to determine home directory")?;
51
52 Ok(home_dir.join(".omni-dev").join("settings.json"))
53 }
54
55 pub fn get_env_var(&self, key: &str) -> Option<String> {
57 match env::var(key) {
59 Ok(value) => Some(value),
60 Err(_) => {
61 self.env.get(key).cloned()
63 }
64 }
65 }
66}
67
68pub fn get_env_var(key: &str) -> Result<String> {
70 match env::var(key) {
72 Ok(value) => Ok(value),
73 Err(_) => {
74 match Settings::load() {
76 Ok(settings) => settings
77 .env
78 .get(key)
79 .cloned()
80 .ok_or_else(|| anyhow::anyhow!("Environment variable not found: {key}")),
81 Err(err) => {
82 Err(anyhow::anyhow!("Environment variable not found: {key}").context(err))
84 }
85 }
86 }
87 }
88}
89
90pub fn get_env_vars(keys: &[&str]) -> Result<String> {
92 for key in keys {
93 if let Ok(value) = get_env_var(key) {
94 return Ok(value);
95 }
96 }
97
98 Err(anyhow::anyhow!(
99 "None of the environment variables found: {keys:?}"
100 ))
101}
102
103#[cfg(test)]
104#[allow(clippy::unwrap_used, clippy::expect_used)]
105mod tests {
106 use super::*;
107 use std::fs;
108 use tempfile::TempDir;
109
110 #[test]
111 fn settings_load_from_path() {
112 let temp_dir = {
114 std::fs::create_dir_all("tmp").ok();
115 TempDir::new_in("tmp").unwrap()
116 };
117 let settings_path = temp_dir.path().join("settings.json");
118
119 let settings_json = r#"{
121 "env": {
122 "TEST_VAR": "test_value",
123 "CLAUDE_API_KEY": "test_api_key"
124 }
125 }"#;
126 fs::write(&settings_path, settings_json).unwrap();
127
128 let settings = Settings::load_from_path(&settings_path).unwrap();
130
131 assert_eq!(settings.env.get("TEST_VAR").unwrap(), "test_value");
133 assert_eq!(settings.env.get("CLAUDE_API_KEY").unwrap(), "test_api_key");
134 }
135
136 #[test]
137 fn settings_get_env_var() {
138 let temp_dir = {
140 std::fs::create_dir_all("tmp").ok();
141 TempDir::new_in("tmp").unwrap()
142 };
143 let settings_path = temp_dir.path().join("settings.json");
144
145 let settings_json = r#"{
147 "env": {
148 "TEST_VAR": "test_value",
149 "CLAUDE_API_KEY": "test_api_key"
150 }
151 }"#;
152 fs::write(&settings_path, settings_json).unwrap();
153
154 let settings = Settings::load_from_path(&settings_path).unwrap();
156
157 env::set_var("TEST_VAR_ENV", "env_value");
159
160 env::set_var("TEST_VAR", "env_override");
162 assert_eq!(settings.get_env_var("TEST_VAR").unwrap(), "env_override");
163
164 env::remove_var("TEST_VAR"); assert_eq!(settings.get_env_var("TEST_VAR").unwrap(), "test_value");
167
168 assert_eq!(settings.get_env_var("TEST_VAR_ENV").unwrap(), "env_value");
170
171 env::remove_var("TEST_VAR_ENV");
173 }
174}