Skip to main content

vespertide_loader/
config.rs

1use std::fs;
2use std::path::{Path, PathBuf};
3
4use anyhow::{Context, Result};
5use vespertide_config::VespertideConfig;
6
7/// Read and parse a vespertide.json file that is known to exist.
8fn read_config(path: &Path) -> Result<VespertideConfig> {
9    let content = fs::read_to_string(path).context("read vespertide.json")?;
10    let config: VespertideConfig =
11        serde_json::from_str(&content).context("parse vespertide.json")?;
12    Ok(config)
13}
14
15/// Load vespertide.json config from current directory.
16pub fn load_config() -> Result<VespertideConfig> {
17    let path = PathBuf::from("vespertide.json");
18    if !path.exists() {
19        anyhow::bail!("vespertide.json not found. Run 'vespertide init' first.");
20    }
21
22    read_config(&path)
23}
24
25/// Load config from a specific path.
26pub fn load_config_from_path(path: impl AsRef<Path>) -> Result<VespertideConfig> {
27    let path = path.as_ref();
28    if !path.exists() {
29        anyhow::bail!("vespertide.json not found at: {}", path.display());
30    }
31
32    read_config(path)
33}
34
35/// Load config from project root, with fallback to defaults.
36pub fn load_config_or_default(project_root: Option<PathBuf>) -> Result<VespertideConfig> {
37    let config_path = if let Some(root) = project_root {
38        root.join("vespertide.json")
39    } else {
40        PathBuf::from("vespertide.json")
41    };
42
43    if config_path.exists() {
44        load_config_from_path(config_path)
45    } else {
46        Ok(VespertideConfig::default())
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use crate::test_support::{CwdGuard, write_default_config};
54    use serial_test::serial;
55    use tempfile::tempdir;
56
57    #[test]
58    #[serial]
59    fn test_load_config_from_path_success() {
60        let tmp = tempdir().unwrap();
61        let config_path = tmp.path().join("vespertide.json");
62        write_default_config(&config_path);
63
64        let result = load_config_from_path(config_path);
65        assert!(result.is_ok());
66        let config = result.unwrap();
67        assert_eq!(config.models_dir, PathBuf::from("models"));
68    }
69
70    #[test]
71    #[serial]
72    fn test_load_config_from_path_not_found() {
73        let tmp = tempdir().unwrap();
74        let config_path = tmp.path().join("nonexistent.json");
75
76        let result = load_config_from_path(config_path.clone());
77        assert!(result.is_err());
78        let err_msg = result.unwrap_err().to_string();
79        assert!(err_msg.contains("vespertide.json not found at:"));
80        assert!(err_msg.contains(&config_path.display().to_string()));
81    }
82
83    #[test]
84    #[serial]
85    fn test_load_config_or_default_with_root() {
86        let tmp = tempdir().unwrap();
87        let config_path = tmp.path().join("vespertide.json");
88        write_default_config(&config_path);
89
90        let result = load_config_or_default(Some(tmp.path().to_path_buf()));
91        assert!(result.is_ok());
92        let config = result.unwrap();
93        assert_eq!(config.models_dir, PathBuf::from("models"));
94    }
95
96    #[test]
97    #[serial]
98    fn test_load_config_or_default_without_root() {
99        let tmp = tempdir().unwrap();
100        let _guard = CwdGuard::new(tmp.path());
101        let config_path = PathBuf::from("vespertide.json");
102        write_default_config(&config_path);
103
104        let result = load_config_or_default(None);
105        assert!(result.is_ok());
106        let config = result.unwrap();
107        assert_eq!(config.models_dir, PathBuf::from("models"));
108    }
109
110    #[test]
111    #[serial]
112    fn test_load_config_or_default_fallback_to_default() {
113        let tmp = tempdir().unwrap();
114        let _guard = CwdGuard::new(tmp.path());
115
116        let result = load_config_or_default(None);
117        assert!(result.is_ok());
118        let config = result.unwrap();
119        assert_eq!(config.models_dir, PathBuf::from("models"));
120    }
121
122    #[test]
123    #[serial]
124    fn test_load_config_success() {
125        let tmp = tempdir().unwrap();
126        let _guard = CwdGuard::new(tmp.path());
127        let config_path = PathBuf::from("vespertide.json");
128        write_default_config(&config_path);
129
130        let result = load_config();
131        assert!(result.is_ok());
132        let config = result.unwrap();
133        assert_eq!(config.models_dir, PathBuf::from("models"));
134    }
135
136    #[test]
137    #[serial]
138    fn test_load_config_not_found() {
139        let tmp = tempdir().unwrap();
140        let _guard = CwdGuard::new(tmp.path());
141
142        let result = load_config();
143        assert!(result.is_err());
144        let err_msg = result.unwrap_err().to_string();
145        assert!(err_msg.contains("vespertide.json not found"));
146    }
147}