ricecoder_refactoring/config/
loader.rs

1//! Configuration loader for refactoring rules
2
3use crate::error::{RefactoringError, Result};
4use crate::types::RefactoringConfig;
5use std::path::Path;
6
7/// Loads refactoring configuration from files
8pub struct ConfigLoader;
9
10impl ConfigLoader {
11    /// Load configuration from a YAML file
12    pub fn load_from_yaml(path: &Path) -> Result<RefactoringConfig> {
13        let content = std::fs::read_to_string(path)
14            .map_err(|e| RefactoringError::ConfigError(format!("Failed to read config file: {}", e)))?;
15
16        serde_yaml::from_str(&content)
17            .map_err(|e| RefactoringError::ConfigError(format!("Failed to parse YAML: {}", e)))
18    }
19
20    /// Load configuration from a JSON file
21    pub fn load_from_json(path: &Path) -> Result<RefactoringConfig> {
22        let content = std::fs::read_to_string(path)
23            .map_err(|e| RefactoringError::ConfigError(format!("Failed to read config file: {}", e)))?;
24
25        serde_json::from_str(&content)
26            .map_err(|e| RefactoringError::ConfigError(format!("Failed to parse JSON: {}", e)))
27    }
28
29    /// Load configuration from a file (auto-detect format)
30    pub fn load(path: &Path) -> Result<RefactoringConfig> {
31        match path.extension().and_then(|ext| ext.to_str()) {
32            Some("yaml") | Some("yml") => Self::load_from_yaml(path),
33            Some("json") => Self::load_from_json(path),
34            _ => Err(RefactoringError::ConfigError(
35                "Unsupported configuration file format".to_string(),
36            )),
37        }
38    }
39
40    /// Validate configuration
41    pub fn validate(config: &RefactoringConfig) -> Result<()> {
42        if config.language.is_empty() {
43            return Err(RefactoringError::InvalidConfiguration(
44                "Language name cannot be empty".to_string(),
45            ));
46        }
47
48        if config.extensions.is_empty() {
49            return Err(RefactoringError::InvalidConfiguration(
50                "At least one file extension must be specified".to_string(),
51            ));
52        }
53
54        Ok(())
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn test_load_yaml_config() -> Result<()> {
64        use tempfile::TempDir;
65        let dir = TempDir::new()?;
66        let file_path = dir.path().join("config.yaml");
67        
68        let yaml_content = r#"
69language: rust
70extensions:
71  - .rs
72rules:
73  - name: "unused_variable"
74    pattern: "let \\w+ = .*;"
75    refactoring_type: RemoveUnused
76    enabled: true
77transformations: []
78"#;
79        std::fs::write(&file_path, yaml_content)?;
80
81        let config = ConfigLoader::load(&file_path)?;
82        assert_eq!(config.language, "rust");
83        assert_eq!(config.extensions, vec![".rs"]);
84        assert_eq!(config.rules.len(), 1);
85
86        Ok(())
87    }
88
89    #[test]
90    fn test_validate_config() -> Result<()> {
91        let config = RefactoringConfig {
92            language: "rust".to_string(),
93            extensions: vec![".rs".to_string()],
94            rules: vec![],
95            transformations: vec![],
96            provider: None,
97        };
98
99        ConfigLoader::validate(&config)?;
100        Ok(())
101    }
102
103    #[test]
104    fn test_validate_config_empty_language() {
105        let config = RefactoringConfig {
106            language: "".to_string(),
107            extensions: vec![".rs".to_string()],
108            rules: vec![],
109            transformations: vec![],
110            provider: None,
111        };
112
113        assert!(ConfigLoader::validate(&config).is_err());
114    }
115}