vika_cli/config/
loader.rs

1use crate::config::model::Config;
2use crate::error::{ConfigError, FileSystemError, Result, VikaError};
3use std::path::PathBuf;
4
5const CONFIG_FILE: &str = ".vika.json";
6
7pub fn load_config() -> Result<Config> {
8    let config_path = PathBuf::from(CONFIG_FILE);
9
10    if !config_path.exists() {
11        return Ok(Config::default());
12    }
13
14    let content = std::fs::read_to_string(&config_path)
15        .map_err(|e| VikaError::from(ConfigError::ReadError(e)))?;
16
17    let config: Config =
18        serde_json::from_str(&content).map_err(|e| VikaError::from(ConfigError::ParseError(e)))?;
19
20    Ok(config)
21}
22
23pub fn save_config(config: &Config) -> Result<()> {
24    let config_path = PathBuf::from(CONFIG_FILE);
25
26    // Ensure $schema is set (use default if not present)
27    let mut config_to_save = config.clone();
28    if config_to_save.schema.is_empty() {
29        config_to_save.schema = crate::config::model::default_schema();
30    }
31
32    let content = serde_json::to_string_pretty(&config_to_save)
33        .map_err(|e| VikaError::from(ConfigError::ParseError(e)))?;
34
35    // Ensure parent directory exists
36    if let Some(parent) = config_path.parent() {
37        std::fs::create_dir_all(parent).map_err(|e| {
38            VikaError::from(FileSystemError::CreateDirectoryFailed {
39                path: parent.to_string_lossy().to_string(),
40                source: e,
41            })
42        })?;
43    }
44
45    std::fs::write(&config_path, content).map_err(|e| {
46        VikaError::from(FileSystemError::WriteFileFailed {
47            path: config_path.to_string_lossy().to_string(),
48            source: e,
49        })
50    })?;
51
52    Ok(())
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use std::env;
59
60    #[test]
61    fn test_save_and_load_config() {
62        let temp_dir = tempfile::tempdir().unwrap();
63        let original_dir = env::current_dir().ok();
64
65        let _ = env::set_current_dir(&temp_dir);
66
67        // Save config should succeed
68        let mut config = Config::default();
69        config.specs = vec![crate::config::model::SpecEntry {
70            name: "test".to_string(),
71            path: "test.yaml".to_string(),
72            schemas: crate::config::model::SchemasConfig::default(),
73            apis: crate::config::model::ApisConfig::default(),
74            hooks: None,
75            modules: crate::config::model::ModulesConfig::default(),
76        }];
77        if save_config(&config).is_ok() {
78            // Load config should succeed and match
79            if let Ok(loaded) = load_config() {
80                assert_eq!(loaded.root_dir, config.root_dir);
81                assert_eq!(loaded.specs.len(), config.specs.len());
82            }
83        }
84
85        if let Some(orig) = original_dir {
86            let _ = env::set_current_dir(orig);
87        }
88    }
89
90    #[test]
91    fn test_load_config_not_exists() {
92        let temp_dir = tempfile::tempdir().unwrap();
93        let original_dir = env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("."));
94
95        env::set_current_dir(&temp_dir).unwrap();
96
97        let config = load_config().unwrap();
98        // Should return default config when file doesn't exist
99        assert_eq!(config.root_dir, "src");
100
101        env::set_current_dir(original_dir).unwrap();
102    }
103
104    #[test]
105    fn test_save_config_with_empty_schema() {
106        let temp_dir = tempfile::tempdir().unwrap();
107        let original_dir = env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("."));
108
109        env::set_current_dir(&temp_dir).unwrap();
110
111        let config = Config {
112            schema: String::new(),
113            ..Default::default()
114        };
115        save_config(&config).unwrap();
116
117        let loaded = load_config().unwrap();
118        // Schema should be set to default
119        assert!(!loaded.schema.is_empty());
120
121        env::set_current_dir(original_dir).unwrap();
122    }
123}