1use lazy_static::lazy_static;
3use rustc_hash::FxHashMap;
4use serde::{Deserialize, Serialize};
5
6use crate::diagnostician::DiagnosticAssessor;
7
8use super::{
9 Diagnostic,
10 Severity::{self, Error, Ignore, Info, Warning},
11};
12
13macro_rules! add_diagnostic {
14 ($($number:ident, $severity:expr, $desc:expr,)*) => {
15 { let mut m : FxHashMap<&str, DiagnosticEntry> = FxHashMap::default();
16 $(
17 {
18 let code = stringify!($number);
19 m.insert(code, DiagnosticEntry{ code, severity: $severity, description: $desc});
20 }
21 )*
22 m
23 }}
24}
25
26pub struct DiagnosticsRegistry(FxHashMap<&'static str, DiagnosticEntry>);
27
28#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
29pub struct DiagnosticEntry {
30 code: &'static str,
31 severity: Severity,
32 description: &'static str,
33}
34
35impl Default for DiagnosticsRegistry {
36 fn default() -> Self {
37 Self::new(DIAGNOSTICS.clone())
38 }
39}
40
41impl DiagnosticsRegistry {
42 fn new(map: FxHashMap<&'static str, DiagnosticEntry>) -> Self {
44 DiagnosticsRegistry(map)
45 }
46
47 pub fn with_configuration(mut self, config: DiagnosticsConfiguration) -> Self {
48 for (severity, codes) in config.0 {
49 for code in &codes {
50 if let Some(entry) = self.0.get_mut(code.as_str()) {
51 entry.severity = severity
52 }
53 }
54 }
55 self
56 }
57}
58
59impl DiagnosticAssessor for DiagnosticsRegistry {
60 fn assess(&self, d: &Diagnostic) -> Severity {
63 self.0.get(d.get_error_code()).map(|it| it.severity).unwrap_or_else(|| {
64 log::warn!("Unrecognized error code {}. Using default severity", d.get_error_code());
65 Severity::default()
66 })
67 }
68
69 fn explain(&self, error: &str) -> String {
70 let Some(info) = self.0.get(error) else {
71 return format!("Unknown error {error}");
72 };
73 format!(
74 r"Explanation for error {error}:
75{}
76",
77 info.description
78 )
79 }
80
81 fn get_diagnostic_configuration(&self) -> String {
82 let config: DiagnosticsConfiguration = self.into();
83 serde_json::ser::to_string(&config).expect("Cannot fail")
84 }
85}
86
87#[derive(Serialize, Deserialize, PartialEq, Eq, Default)]
88#[serde(transparent)]
89pub struct DiagnosticsConfiguration(FxHashMap<Severity, Vec<String>>);
90
91impl From<&DiagnosticsRegistry> for DiagnosticsConfiguration {
92 fn from(registry: &DiagnosticsRegistry) -> Self {
93 let mut res = DiagnosticsConfiguration::default();
94 for val in registry.0.values() {
95 let entry = res.0.entry(val.severity).or_default();
96 entry.push(val.code.into());
97 }
98 res
99 }
100}
101
102#[rustfmt::skip]
103lazy_static! {
104 pub static ref DIAGNOSTICS: FxHashMap<&'static str, DiagnosticEntry> = add_diagnostic!(
105 E001, Error, include_str!("./error_codes/E001.md"), E002, Error, include_str!("./error_codes/E002.md"), E003, Error, include_str!("./error_codes/E003.md"), E004, Error, include_str!("./error_codes/E004.md"), E005, Error, include_str!("./error_codes/E005.md"), E006, Error, include_str!("./error_codes/E006.md"), E007, Error, include_str!("./error_codes/E007.md"), E008, Error, include_str!("./error_codes/E008.md"), E009, Error, include_str!("./error_codes/E009.md"), E010, Error, include_str!("./error_codes/E010.md"), E011, Error, include_str!("./error_codes/E011.md"), E012, Error, include_str!("./error_codes/E012.md"), E013, Warning, include_str!("./error_codes/E013.md"), E014, Warning, include_str!("./error_codes/E014.md"), E015, Ignore, include_str!("./error_codes/E015.md"), E016, Warning, include_str!("./error_codes/E016.md"), E017, Error, include_str!("./error_codes/E017.md"), E018, Error, include_str!("./error_codes/E018.md"), E019, Error, include_str!("./error_codes/E019.md"), E020, Error, include_str!("./error_codes/E020.md"), E021, Error, include_str!("./error_codes/E021.md"), E022, Warning, include_str!("./error_codes/E022.md"), E023, Warning, include_str!("./error_codes/E023.md"), E024, Warning, include_str!("./error_codes/E024.md"), E025, Error, include_str!("./error_codes/E025.md"), E026, Error, include_str!("./error_codes/E026.md"),
131 E027, Error, include_str!("./error_codes/E027.md"),
132 E028, Error, include_str!("./error_codes/E028.md"),
133 E029, Error, include_str!("./error_codes/E029.md"),
134 E030, Error, include_str!("./error_codes/E030.md"),
135 E031, Error, include_str!("./error_codes/E031.md"),
136 E032, Error, include_str!("./error_codes/E032.md"),
137 E033, Error, include_str!("./error_codes/E033.md"),
138 E034, Error, include_str!("./error_codes/E034.md"),
139 E035, Error, include_str!("./error_codes/E035.md"),
140 E036, Error, include_str!("./error_codes/E036.md"),
141 E037, Error, include_str!("./error_codes/E037.md"),
142 E038, Error, include_str!("./error_codes/E038.md"), E039, Warning, include_str!("./error_codes/E039.md"),
144 E040, Warning, include_str!("./error_codes/E040.md"),
145 E041, Error, include_str!("./error_codes/E041.md"),
146 E042, Warning, include_str!("./error_codes/E042.md"), E043, Error, include_str!("./error_codes/E043.md"),
148 E044, Error, include_str!("./error_codes/E044.md"),
149 E045, Error, include_str!("./error_codes/E045.md"),
150 E046, Error, include_str!("./error_codes/E046.md"),
151 E047, Warning, include_str!("./error_codes/E047.md"), E048, Error, include_str!("./error_codes/E048.md"),
153 E049, Warning, include_str!("./error_codes/E049.md"),
154 E050, Error, include_str!("./error_codes/E050.md"),
155 E051, Error, include_str!("./error_codes/E051.md"),
156 E052, Error, include_str!("./error_codes/E052.md"),
157 E053, Error, include_str!("./error_codes/E053.md"),
158 E054, Error, include_str!("./error_codes/E054.md"),
159 E055, Error, include_str!("./error_codes/E055.md"),
160 E056, Error, include_str!("./error_codes/E056.md"),
161 E057, Error, include_str!("./error_codes/E057.md"),
162 E058, Error, include_str!("./error_codes/E058.md"),
163 E059, Error, include_str!("./error_codes/E059.md"),
164 E060, Info, include_str!("./error_codes/E060.md"), E061, Error, include_str!("./error_codes/E061.md"),
166 E062, Error, include_str!("./error_codes/E062.md"),
167 E063, Error, include_str!("./error_codes/E063.md"),
168 E064, Error, include_str!("./error_codes/E064.md"),
169 E065, Error, include_str!("./error_codes/E065.md"),
170 E066, Error, include_str!("./error_codes/E066.md"),
171 E067, Warning, include_str!("./error_codes/E067.md"), E068, Error, include_str!("./error_codes/E068.md"),
173 E069, Error, include_str!("./error_codes/E069.md"),
174 E070, Error, include_str!("./error_codes/E070.md"),
175 E071, Error, include_str!("./error_codes/E071.md"),
176 E072, Error, include_str!("./error_codes/E072.md"),
177 E073, Error, include_str!("./error_codes/E073.md"),
178 E074, Error, include_str!("./error_codes/E074.md"),
179 E075, Error, include_str!("./error_codes/E075.md"),
180 E076, Error, include_str!("./error_codes/E076.md"),
181 E077, Error, include_str!("./error_codes/E077.md"),
182 E078, Error, include_str!("./error_codes/E078.md"),
183 E079, Error, include_str!("./error_codes/E079.md"),
184 E080, Error, include_str!("./error_codes/E080.md"),
185 E081, Error, include_str!("./error_codes/E081.md"), E082, Error, include_str!("./error_codes/E082.md"), E083, Error, include_str!("./error_codes/E083.md"), E084, Warning, include_str!("./error_codes/E084.md"), E085, Error, include_str!("./error_codes/E085.md"), E086, Error, include_str!("./error_codes/E086.md"), E087, Error, include_str!("./error_codes/E087.md"),
192 E088, Error, include_str!("./error_codes/E088.md"),
193 E089, Error, include_str!("./error_codes/E089.md"),
194 E090, Warning, include_str!("./error_codes/E090.md"), E091, Warning, include_str!("./error_codes/E091.md"),
196 E092, Info, include_str!("./error_codes/E092.md"),
197 E093, Warning, include_str!("./error_codes/E093.md"),
198 E094, Error, include_str!("./error_codes/E094.md"),
199 E095, Error, include_str!("./error_codes/E095.md"), E096, Warning, include_str!("./error_codes/E096.md"), E097, Error, include_str!("./error_codes/E097.md"), E098, Error, include_str!("./error_codes/E098.md"), E099, Error, include_str!("./error_codes/E099.md"), E100, Error, include_str!("./error_codes/E100.md"), E101, Error, include_str!("./error_codes/E101.md"), E102, Error, include_str!("./error_codes/E102.md"), E103, Error, include_str!("./error_codes/E103.md"), E104, Error, include_str!("./error_codes/E104.md"), E105, Error, include_str!("./error_codes/E105.md"), E106, Warning, include_str!("./error_codes/E106.md"), E107, Error, include_str!("./error_codes/E107.md"), E108, Error, include_str!("./error_codes/E108.md"), E109, Error, include_str!("./error_codes/E109.md"), E110, Error, include_str!("./error_codes/E110.md"), E111, Error, include_str!("./error_codes/E111.md"), E112, Error, include_str!("./error_codes/E112.md"), E113, Error, include_str!("./error_codes/E113.md"), E114, Error, include_str!("./error_codes/E114.md"), E115, Error, include_str!("./error_codes/E115.md"), E116, Error, include_str!("./error_codes/E116.md"), E118, Info, include_str!("./error_codes/E118.md"), E119, Error, include_str!("./error_codes/E119.md"), E120, Error, include_str!("./error_codes/E120.md"), E121, Error, include_str!("./error_codes/E121.md"), E122, Error, include_str!("./error_codes/E122.md"), E123, Error, include_str!("./error_codes/E123.md"), E124, Error, include_str!("./error_codes/E124.md"), E125, Error, include_str!("./error_codes/E125.md"), E126, Error, include_str!("./error_codes/E126.md"), E127, Warning, include_str!("./error_codes/E127.md"), E128, Error, include_str!("./error_codes/E128.md"), E129, Error, include_str!("./error_codes/E129.md"), E130, Error, include_str!("./error_codes/E130.md"), E117, Error, include_str!("./error_codes/E117.md"), E131, Error, include_str!("./error_codes/E131.md"), E132, Ignore, include_str!("./error_codes/E132.md"), E133, Error, include_str!("./error_codes/E133.md"), E134, Error, include_str!("./error_codes/E134.md"), E135, Warning, include_str!("./error_codes/E135.md"), E136, Error, include_str!("./error_codes/E136.md"), E137, Error, include_str!("./error_codes/E137.md"), E138, Error, include_str!("./error_codes/E138.md"), E139, Error, include_str!("./error_codes/E139.md"), E140, Error, include_str!("./error_codes/E140.md"), E141, Error, include_str!("./error_codes/E141.md"), E142, Error, include_str!("./error_codes/E142.md"), E143, Warning, include_str!("./error_codes/E143.md"), E144, Error, include_str!("./error_codes/E144.md"), E145, Warning, include_str!("./error_codes/E145.md"), E146, Error, include_str!("./error_codes/E146.md"), E147, Error, include_str!("./error_codes/E147.md"), E148, Warning, include_str!("./error_codes/E148.md"), E149, Error, include_str!("./error_codes/E149.md"), E150, Warning, include_str!("./error_codes/E150.md"), E151, Error, include_str!("./error_codes/E151.md"), E152, Error, include_str!("./error_codes/E152.md"), E153, Error, include_str!("./error_codes/E153.md"), E154, Error, include_str!("./error_codes/E154.md"), E155, Error, include_str!("./error_codes/E155.md"), );
261}
262
263#[cfg(test)]
264mod tests {
265 use crate::{
266 diagnostician::DiagnosticAssessor,
267 diagnostics::{diagnostics_registry::DiagnosticsConfiguration, Diagnostic, Severity},
268 };
269
270 use super::DiagnosticsRegistry;
271
272 #[test]
273 fn deserialize_empty_json() {
274 let error_config = "{}";
275
276 let DiagnosticsConfiguration(configuration) = serde_json::de::from_str(error_config).unwrap();
277 assert!(configuration.is_empty());
278 }
279
280 #[test]
281 fn deserialize_json() {
282 let error_config = r#"{
283 "error": ["E001", "E002"],
284 "warning": ["E003", "E004"],
285 "info": ["E005"],
286 "ignore": ["E010"]
287 }"#;
288
289 let DiagnosticsConfiguration(configuration) = serde_json::de::from_str(error_config).unwrap();
290 assert_eq!(configuration.len(), 4);
291 assert_eq!(configuration.get(&Severity::Error).unwrap(), &["E001", "E002"]);
292 assert_eq!(configuration.get(&Severity::Warning).unwrap(), &["E003", "E004"]);
293 assert_eq!(configuration.get(&Severity::Info).unwrap(), &["E005"]);
294 assert_eq!(configuration.get(&Severity::Ignore).unwrap(), &["E010"]);
295 }
296
297 #[test]
298 fn overridden_errors_are_assessed_correctly() {
299 let error_config = r#"{
300 "error": ["E090"],
301 "warning": ["E001"],
302 "info": ["E002"],
303 "ignore": ["E003"]
304 }"#;
305
306 let configuration = serde_json::de::from_str(error_config).unwrap();
307 let diagnostics_registry = DiagnosticsRegistry::default().with_configuration(configuration);
308
309 let e090 = Diagnostic::new("Warning->Error").with_error_code("E090");
310 let e001 = Diagnostic::new("Error->Warning").with_error_code("E001");
311 let e002 = Diagnostic::new("Error->Info").with_error_code("E002");
312 let e003 = Diagnostic::new("Error->Ignore").with_error_code("E003");
313
314 assert_eq!(diagnostics_registry.assess(&e090), Severity::Error);
315 assert_eq!(diagnostics_registry.assess(&e001), Severity::Warning);
316 assert_eq!(diagnostics_registry.assess(&e002), Severity::Info);
317 assert_eq!(diagnostics_registry.assess(&e003), Severity::Ignore);
318 }
319}