1use serde::{Deserialize, Serialize};
4use std::path::Path;
5
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
7#[serde(rename_all = "kebab-case")]
8pub struct Config {
9 #[serde(default)]
10 pub profile: ConfigProfile,
11 #[serde(default)]
12 pub collectors: ConfigCollectors,
13 #[serde(default)]
14 pub gate: ConfigGate,
15 #[serde(default)]
16 pub output: ConfigOutput,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
20#[serde(rename_all = "kebab-case")]
21pub struct ConfigProfile {
22 #[serde(default = "default_profile_default")]
23 pub default: String,
24}
25
26fn default_profile_default() -> String {
27 "full".to_string()
28}
29
30impl Default for ConfigProfile {
31 fn default() -> Self {
32 Self {
33 default: default_profile_default(),
34 }
35 }
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
39#[serde(rename_all = "kebab-case")]
40pub struct ConfigCollectors {
41 #[serde(default)]
42 pub mutants: Option<bool>,
43 #[serde(default)]
44 pub hack: Option<bool>,
45 #[serde(default)]
46 pub coverage: Option<bool>,
47 #[serde(default)]
48 pub deny: Option<bool>,
49 #[serde(default)]
50 pub audit: Option<bool>,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
54#[serde(rename_all = "kebab-case")]
55pub struct ConfigGate {
56 #[serde(default)]
57 pub coverage: Option<ConfigGateCoverage>,
58 #[serde(default)]
59 pub size: Option<SizeConfig>,
60 #[serde(default)]
61 pub complexity: Option<ComplexityConfig>,
62 #[serde(default)]
63 pub defaults: Option<ConfigGateDefaults>,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
67pub struct ConfigGateCoverage {
68 #[serde(default)]
69 pub min_line_percent: Option<f64>,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
75#[serde(rename_all = "kebab-case")]
76pub struct ConfigGateDefaults {
77 #[serde(default)]
79 pub max_cyclomatic_per_function: Option<u32>,
80 #[serde(default)]
82 pub max_nesting_depth: Option<u32>,
83 #[serde(default)]
85 pub max_lines_per_function: Option<u32>,
86 #[serde(default)]
88 pub max_lines_per_file: Option<u32>,
89 #[serde(default)]
91 pub max_code_lines_per_file: Option<u32>,
92 #[serde(default)]
94 pub max_parameters_per_function: Option<u32>,
95 #[serde(default)]
97 pub min_coverage_percent: Option<f64>,
98 #[serde(default)]
100 pub max_duplicate_lines: Option<u32>,
101 #[serde(default)]
103 pub max_clippy_warnings: Option<u32>,
104 #[serde(default)]
106 pub max_line_length: Option<usize>,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
111#[serde(rename_all = "kebab-case")]
112pub struct SizeConfig {
113 #[serde(default)]
115 pub max_lines_per_file: Option<u32>,
116 #[serde(default)]
118 pub max_code_lines_per_file: Option<u32>,
119 #[serde(default)]
121 pub max_lines_per_function: Option<u32>,
122 #[serde(default)]
124 pub max_parameters_per_function: Option<u32>,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
129#[serde(rename_all = "kebab-case")]
130pub struct ComplexityConfig {
131 #[serde(default)]
133 pub max_cyclomatic_per_function: Option<u32>,
134 #[serde(default)]
136 pub max_nesting_depth: Option<u32>,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
140#[serde(rename_all = "kebab-case")]
141pub struct ConfigOutput {
142 #[serde(default)]
143 pub dir: Option<String>,
144}
145
146impl Config {
147 pub fn load(path: &Path) -> anyhow::Result<Self> {
149 let contents = std::fs::read_to_string(path)?;
150 let config: Config = toml::from_str(&contents)?;
151 Ok(config)
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 const EXAMPLE_CONFIG: &str = r#"
160[profile]
161default = "deep"
162
163[collectors]
164mutants = false
165
166[gate.coverage]
167min_line_percent = 80.0
168
169[output]
170dir = "quality"
171"#;
172
173 #[test]
174 fn test_parse_config() {
175 let config: Config = toml::from_str(EXAMPLE_CONFIG).unwrap();
176 assert_eq!(config.profile.default, "deep");
177 assert_eq!(config.collectors.mutants, Some(false));
178 assert_eq!(
179 config.gate.coverage.as_ref().unwrap().min_line_percent,
180 Some(80.0)
181 );
182 assert_eq!(config.output.dir.as_deref(), Some("quality"));
183 }
184}