1use anyhow::Result;
2use serde::Deserialize;
3use std::collections::HashSet;
4use std::fs;
5use std::path::Path;
6
7#[derive(Debug, Clone, Deserialize, Default)]
9pub struct Config {
10 #[serde(default)]
12 pub exclude: HashSet<String>,
13
14 #[serde(default)]
16 pub essential: HashSet<String>,
17
18 #[serde(default)]
20 pub dev_only: HashSet<String>,
21
22 #[serde(default)]
24 pub skip_tests: bool,
25}
26
27impl Config {
28 pub fn load(path: &Path) -> Result<Self> {
30 if path.exists() {
31 let content = fs::read_to_string(path)?;
32 let config: Config = toml::from_str(&content)?;
33 Ok(config)
34 } else {
35 Ok(Self::default())
36 }
37 }
38
39 pub fn load_default(project_root: &Path) -> Result<Self> {
41 let config_path = project_root.join(".cargo-autodd.toml");
42 Self::load(&config_path)
43 }
44
45 pub fn should_exclude(&self, crate_name: &str) -> bool {
47 self.exclude.contains(crate_name)
48 }
49
50 pub fn is_essential(&self, crate_name: &str) -> bool {
52 self.essential.contains(crate_name)
53 }
54
55 pub fn is_dev_only(&self, crate_name: &str) -> bool {
57 self.dev_only.contains(crate_name)
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64 use std::io::Write;
65 use tempfile::TempDir;
66
67 #[test]
68 fn test_load_default_config() -> Result<()> {
69 let temp_dir = TempDir::new()?;
70 let config = Config::load_default(temp_dir.path())?;
71 assert!(config.exclude.is_empty());
72 assert!(config.essential.is_empty());
73 assert!(config.dev_only.is_empty());
74 assert!(!config.skip_tests);
75 Ok(())
76 }
77
78 #[test]
79 fn test_load_config_file() -> Result<()> {
80 let temp_dir = TempDir::new()?;
81 let config_path = temp_dir.path().join(".cargo-autodd.toml");
82
83 let config_content = r#"
84exclude = ["internal_crate", "another_crate"]
85essential = ["custom_essential"]
86dev_only = ["proptest", "criterion"]
87skip_tests = true
88"#;
89
90 let mut file = fs::File::create(&config_path)?;
91 write!(file, "{}", config_content)?;
92
93 let config = Config::load(&config_path)?;
94 assert!(config.should_exclude("internal_crate"));
95 assert!(config.should_exclude("another_crate"));
96 assert!(!config.should_exclude("external_crate"));
97 assert!(config.is_essential("custom_essential"));
98 assert!(config.is_dev_only("proptest"));
99 assert!(config.is_dev_only("criterion"));
100 assert!(config.skip_tests);
101
102 Ok(())
103 }
104
105 #[test]
106 fn test_partial_config() -> Result<()> {
107 let temp_dir = TempDir::new()?;
108 let config_path = temp_dir.path().join(".cargo-autodd.toml");
109
110 let config_content = r#"
112exclude = ["internal_crate"]
113"#;
114
115 let mut file = fs::File::create(&config_path)?;
116 write!(file, "{}", config_content)?;
117
118 let config = Config::load(&config_path)?;
119 assert!(config.should_exclude("internal_crate"));
120 assert!(config.essential.is_empty());
121 assert!(config.dev_only.is_empty());
122 assert!(!config.skip_tests);
123
124 Ok(())
125 }
126}