1use crate::reporter::Severity;
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use std::fs;
8use std::path::Path;
9
10#[derive(Debug, Serialize, Deserialize, Clone, Default)]
12pub struct Config {
13 #[serde(default)]
15 pub analyzer: AnalyzerSettings,
16
17 #[serde(default)]
19 pub rules: RulesConfig,
20
21 #[serde(default)]
23 pub reporter: ReporterConfig,
24
25 #[serde(default)]
27 pub renderer_analysis: RendererAnalysisConfig,
28
29 #[serde(default, rename = "rule_severity")]
31 rule_severity_tmp: HashMap<String, Severity>,
32}
33
34#[derive(Debug, Serialize, Deserialize, Clone)]
36pub struct AnalyzerSettings {
37 #[serde(default = "default_true")]
39 pub syntax_check: bool,
40
41 #[serde(default = "default_true")]
43 pub semantic_analysis: bool,
44
45 #[serde(default = "default_false")]
47 pub metrics_enabled: bool,
48
49 #[serde(default)]
51 pub enabled_rules: Vec<String>,
52
53 #[serde(default)]
55 pub disabled_rules: Vec<String>,
56
57 #[serde(default = "default_false")]
59 pub parallel: bool,
60
61 #[serde(default = "default_false")]
63 pub incremental: bool,
64}
65
66#[derive(Debug, Serialize, Deserialize, Clone, Default)]
68pub struct RulesConfig {
69 #[serde(default)]
71 pub component_naming: ComponentNamingConfig,
72
73 #[serde(default)]
75 pub rule_severity: HashMap<String, Severity>,
76}
77
78#[derive(Debug, Serialize, Deserialize, Clone)]
80pub struct ComponentNamingConfig {
81 #[serde(default = "default_component_pattern")]
83 pub pattern: String,
84}
85
86#[derive(Debug, Serialize, Deserialize, Clone)]
88pub struct ReporterConfig {
89 #[serde(default = "default_format")]
91 pub format: String,
92
93 pub output_path: Option<String>,
95
96 #[serde(default)]
98 pub min_severity: Severity,
99}
100
101#[derive(Debug, Serialize, Deserialize, Clone)]
103pub struct RendererAnalysisConfig {
104 #[serde(default = "default_true")]
106 pub enabled: bool,
107
108 #[serde(default = "default_renderer")]
110 pub default_renderer: String,
111
112 #[serde(default = "default_true")]
114 pub check_renderer_metadata: bool,
115}
116
117fn default_true() -> bool {
119 true
120}
121
122fn default_false() -> bool {
123 false
124}
125
126fn default_component_pattern() -> String {
127 "^[A-Z][a-zA-Z0-9]*$".to_string()
128}
129
130fn default_format() -> String {
131 "text".to_string()
132}
133
134fn default_renderer() -> String {
135 "auto".to_string()
136}
137
138impl Default for AnalyzerSettings {
141 fn default() -> Self {
142 Self {
143 syntax_check: true,
144 semantic_analysis: true,
145 metrics_enabled: false,
146 enabled_rules: Vec::new(),
147 disabled_rules: Vec::new(),
148 parallel: false,
149 incremental: false,
150 }
151 }
152}
153
154impl Default for ComponentNamingConfig {
157 fn default() -> Self {
158 Self {
159 pattern: default_component_pattern(),
160 }
161 }
162}
163
164impl Default for ReporterConfig {
165 fn default() -> Self {
166 Self {
167 format: default_format(),
168 output_path: None,
169 min_severity: Severity::Warning,
170 }
171 }
172}
173
174impl Default for RendererAnalysisConfig {
175 fn default() -> Self {
176 Self {
177 enabled: true,
178 default_renderer: default_renderer(),
179 check_renderer_metadata: true,
180 }
181 }
182}
183
184impl Config {
185 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, Box<dyn std::error::Error>> {
187 let content = fs::read_to_string(path)?;
188 let mut config: Config = toml::from_str(&content)?;
189
190 if !config.rule_severity_tmp.is_empty() {
192 config.rules.rule_severity = config.rule_severity_tmp.clone();
193 config.rule_severity_tmp.clear();
194 }
195
196 Ok(config)
197 }
198
199 pub fn find_and_load() -> Result<Self, Box<dyn std::error::Error>> {
201 let mut current_dir = std::env::current_dir()?;
203
204 loop {
205 let config_path = current_dir.join(".orlint.toml");
206 if config_path.exists() {
207 return Self::from_file(config_path);
208 }
209
210 if !current_dir.pop() {
211 break;
212 }
213 }
214
215 Ok(Self::default())
217 }
218
219 pub fn is_rule_enabled(&self, rule_name: &str) -> bool {
221 if !self.analyzer.disabled_rules.is_empty()
222 && self
223 .analyzer
224 .disabled_rules
225 .contains(&rule_name.to_string())
226 {
227 return false;
228 }
229
230 if !self.analyzer.enabled_rules.is_empty() {
231 return self.analyzer.enabled_rules.contains(&rule_name.to_string());
232 }
233
234 true
235 }
236
237 pub fn get_rule_severity(&self, rule_name: &str, default_severity: Severity) -> Severity {
239 self.rules
240 .rule_severity
241 .get(rule_name)
242 .cloned()
243 .unwrap_or(default_severity)
244 }
245}