dockerfile_roast/
config.rs1use anyhow::Context;
17use serde::Deserialize;
18use std::path::Path;
19
20#[derive(Debug, Default, Deserialize)]
21#[serde(rename_all = "kebab-case", deny_unknown_fields)]
22pub struct DroastConfig {
23 pub skip: Option<Vec<String>>,
25
26 pub min_severity: Option<String>,
28
29 pub no_roast: Option<bool>,
31
32 pub no_fail: Option<bool>,
34
35 pub format: Option<String>,
37}
38
39impl DroastConfig {
40 pub fn load() -> Self {
42 if let Some(path) = Self::find() {
43 Self::load_from(&path).unwrap_or_default()
44 } else {
45 Self::default()
46 }
47 }
48
49 fn find() -> Option<std::path::PathBuf> {
52 let cwd = std::env::current_dir().ok()?;
53 let mut dir: &Path = &cwd;
54 loop {
55 let candidate = dir.join("droast.toml");
56 if candidate.is_file() {
57 return Some(candidate);
58 }
59 if dir.join(".git").exists() {
61 break;
62 }
63 match dir.parent() {
64 Some(parent) => dir = parent,
65 None => break,
66 }
67 }
68 None
69 }
70
71 pub fn load_from(path: &Path) -> anyhow::Result<Self> {
73 let content = std::fs::read_to_string(path)
74 .with_context(|| format!("Failed to read config file '{}'", path.display()))?;
75 let cfg: DroastConfig = toml::from_str(&content)
76 .map_err(|e| anyhow::anyhow!("Invalid config file '{}': {e}", path.display()))?;
77 Ok(cfg)
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::DroastConfig;
84
85 #[test]
86 fn loads_config_from_explicit_path() {
87 let path = std::env::temp_dir().join(format!(
88 "droast-explicit-config-{}.toml",
89 std::process::id()
90 ));
91 std::fs::write(&path, "skip = [\"DF001\"]\nno-roast = true\n").unwrap();
92
93 let config = DroastConfig::load_from(&path).unwrap();
94
95 assert_eq!(config.skip.unwrap(), ["DF001"]);
96 assert_eq!(config.no_roast, Some(true));
97 std::fs::remove_file(path).unwrap();
98 }
99
100 #[test]
101 fn explicit_config_reports_invalid_toml() {
102 let path = std::env::temp_dir().join(format!(
103 "droast-invalid-config-{}.toml",
104 std::process::id()
105 ));
106 std::fs::write(&path, "skip = [\n").unwrap();
107
108 let error = DroastConfig::load_from(&path).unwrap_err().to_string();
109
110 assert!(error.contains("Invalid config file"));
111 std::fs::remove_file(path).unwrap();
112 }
113}