vespertide_loader/
config.rs1use std::fs;
2use std::path::PathBuf;
3
4use anyhow::{Context, Result};
5use vespertide_config::VespertideConfig;
6
7pub fn load_config() -> Result<VespertideConfig> {
9 let path = PathBuf::from("vespertide.json");
10 if !path.exists() {
11 anyhow::bail!("vespertide.json not found. Run 'vespertide init' first.");
12 }
13
14 let content = fs::read_to_string(&path).context("read vespertide.json")?;
15 let config: VespertideConfig =
16 serde_json::from_str(&content).context("parse vespertide.json")?;
17 Ok(config)
18}
19
20pub fn load_config_from_path(path: PathBuf) -> Result<VespertideConfig> {
22 if !path.exists() {
23 anyhow::bail!("vespertide.json not found at: {}", path.display());
24 }
25
26 let content = fs::read_to_string(&path).context("read vespertide.json")?;
27 let config: VespertideConfig =
28 serde_json::from_str(&content).context("parse vespertide.json")?;
29 Ok(config)
30}
31
32pub fn load_config_or_default(project_root: Option<PathBuf>) -> Result<VespertideConfig> {
34 let config_path = if let Some(root) = project_root {
35 root.join("vespertide.json")
36 } else {
37 PathBuf::from("vespertide.json")
38 };
39
40 if config_path.exists() {
41 load_config_from_path(config_path)
42 } else {
43 Ok(VespertideConfig::default())
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use serial_test::serial;
51 use std::fs;
52 use tempfile::tempdir;
53
54 struct CwdGuard {
55 original: PathBuf,
56 }
57
58 impl CwdGuard {
59 fn new(dir: &PathBuf) -> Self {
60 let original = std::env::current_dir().unwrap();
61 std::env::set_current_dir(dir).unwrap();
62 Self { original }
63 }
64 }
65
66 impl Drop for CwdGuard {
67 fn drop(&mut self) {
68 let _ = std::env::set_current_dir(&self.original);
69 }
70 }
71
72 fn write_config(path: &PathBuf) {
73 let cfg = VespertideConfig::default();
74 let text = serde_json::to_string_pretty(&cfg).unwrap();
75 fs::write(path, text).unwrap();
76 }
77
78 #[test]
79 #[serial]
80 fn test_load_config_from_path_success() {
81 let tmp = tempdir().unwrap();
82 let config_path = tmp.path().join("vespertide.json");
83 write_config(&config_path);
84
85 let result = load_config_from_path(config_path);
86 assert!(result.is_ok());
87 let config = result.unwrap();
88 assert_eq!(config.models_dir, PathBuf::from("models"));
89 }
90
91 #[test]
92 #[serial]
93 fn test_load_config_from_path_not_found() {
94 let tmp = tempdir().unwrap();
95 let config_path = tmp.path().join("nonexistent.json");
96
97 let result = load_config_from_path(config_path.clone());
98 assert!(result.is_err());
99 let err_msg = result.unwrap_err().to_string();
100 assert!(err_msg.contains("vespertide.json not found at:"));
101 assert!(err_msg.contains(&config_path.display().to_string()));
102 }
103
104 #[test]
105 #[serial]
106 fn test_load_config_or_default_with_root() {
107 let tmp = tempdir().unwrap();
108 let config_path = tmp.path().join("vespertide.json");
109 write_config(&config_path);
110
111 let result = load_config_or_default(Some(tmp.path().to_path_buf()));
112 assert!(result.is_ok());
113 let config = result.unwrap();
114 assert_eq!(config.models_dir, PathBuf::from("models"));
115 }
116
117 #[test]
118 #[serial]
119 fn test_load_config_or_default_without_root() {
120 let tmp = tempdir().unwrap();
121 let _guard = CwdGuard::new(&tmp.path().to_path_buf());
122 let config_path = PathBuf::from("vespertide.json");
123 write_config(&config_path);
124
125 let result = load_config_or_default(None);
126 assert!(result.is_ok());
127 let config = result.unwrap();
128 assert_eq!(config.models_dir, PathBuf::from("models"));
129 }
130
131 #[test]
132 #[serial]
133 fn test_load_config_or_default_fallback_to_default() {
134 let tmp = tempdir().unwrap();
135 let _guard = CwdGuard::new(&tmp.path().to_path_buf());
136
137 let result = load_config_or_default(None);
138 assert!(result.is_ok());
139 let config = result.unwrap();
140 assert_eq!(config.models_dir, PathBuf::from("models"));
141 }
142
143 #[test]
144 #[serial]
145 fn test_load_config_success() {
146 let tmp = tempdir().unwrap();
147 let _guard = CwdGuard::new(&tmp.path().to_path_buf());
148 let config_path = PathBuf::from("vespertide.json");
149 write_config(&config_path);
150
151 let result = load_config();
152 assert!(result.is_ok());
153 let config = result.unwrap();
154 assert_eq!(config.models_dir, PathBuf::from("models"));
155 }
156
157 #[test]
158 #[serial]
159 fn test_load_config_not_found() {
160 let tmp = tempdir().unwrap();
161 let _guard = CwdGuard::new(&tmp.path().to_path_buf());
162
163 let result = load_config();
164 assert!(result.is_err());
165 let err_msg = result.unwrap_err().to_string();
166 assert!(err_msg.contains("vespertide.json not found"));
167 }
168}