plc_diagnostics/
diagnostician.rs1use rustc_hash::FxHashMap;
2
3use crate::{
4 diagnostics::{
5 diagnostics_registry::{DiagnosticsConfiguration, DiagnosticsRegistry},
6 Diagnostic, Severity,
7 },
8 reporter::{
9 clang::ClangFormatDiagnosticReporter, codespan::CodeSpanDiagnosticReporter,
10 null::NullDiagnosticReporter, DiagnosticReporter, ResolvedDiagnostics, ResolvedLocation,
11 },
12};
13
14pub struct Diagnostician {
17 reporter: Box<dyn DiagnosticReporter>,
18 assessor: Box<dyn DiagnosticAssessor>,
19 filename_fileid_mapping: FxHashMap<String, usize>,
20}
21
22impl Diagnostician {
23 pub fn register_file(&mut self, id: String, src: String) -> usize {
27 let handle = self.reporter.register(id.clone(), src);
28 self.filename_fileid_mapping.insert(id, handle);
29 handle
30 }
31
32 fn get_file_handle(&self, file_name: Option<&str>) -> Option<usize> {
33 file_name.and_then(|it| self.filename_fileid_mapping.get(it).cloned())
34 }
35
36 pub fn handle(&mut self, diagnostics: &[Diagnostic]) -> Severity {
38 let resolved_diagnostics = diagnostics
39 .iter()
40 .fold(vec![], |mut acc, d| {
41 acc.push(d);
42 acc.extend(d.get_sub_diagnostics());
43 acc
44 })
45 .iter()
46 .map(|d| ResolvedDiagnostics {
47 code: d.get_error_code().to_string(),
48 message: d.get_message().to_string(),
49 severity: self.assess(d),
50 main_location: ResolvedLocation {
51 file_handle: self
52 .get_file_handle(d.get_location().get_file_name().or(Some("<internal>")))
53 .unwrap_or(usize::MAX),
54 span: d.get_location().get_span().clone(),
55 },
56 additional_locations: d.get_secondary_locations().map(|it| {
57 it.iter()
58 .map(|l| ResolvedLocation {
59 file_handle: self
60 .get_file_handle(l.get_file_name().or(Some("<internal>")))
61 .unwrap_or(usize::MAX),
62 span: l.get_span().clone(),
63 })
64 .collect()
65 }),
66 })
67 .collect::<Vec<_>>();
68
69 self.report(resolved_diagnostics.as_slice());
70
71 resolved_diagnostics.iter().map(|it| it.severity).max().unwrap_or_default()
72 }
73
74 pub fn null_diagnostician() -> Diagnostician {
76 Diagnostician {
77 assessor: Box::<DiagnosticsRegistry>::default(),
78 reporter: Box::<NullDiagnosticReporter>::default(),
79 filename_fileid_mapping: FxHashMap::default(),
80 }
81 }
82
83 pub fn buffered() -> Diagnostician {
85 Diagnostician {
86 assessor: Box::<DiagnosticsRegistry>::default(),
87 reporter: Box::new(CodeSpanDiagnosticReporter::buffered()),
88 filename_fileid_mapping: FxHashMap::default(),
89 }
90 }
91
92 pub fn clang_format_diagnostician() -> Diagnostician {
94 Diagnostician {
95 reporter: Box::<ClangFormatDiagnosticReporter>::default(),
96 assessor: Box::<DiagnosticsRegistry>::default(),
97 filename_fileid_mapping: FxHashMap::default(),
98 }
99 }
100
101 pub fn with_configuration(self, configuration: DiagnosticsConfiguration) -> Self {
102 let mut res = self;
103 let registry = DiagnosticsRegistry::default().with_configuration(configuration);
104 res.assessor = Box::new(registry);
105 res
106 }
107
108 pub fn explain(&self, error: &str) -> String {
110 self.assessor.explain(error)
111 }
112 pub fn get_diagnostic_configuration(&self) -> String {
113 self.assessor.get_diagnostic_configuration()
114 }
115}
116
117impl DiagnosticReporter for Diagnostician {
118 fn report(&mut self, diagnostics: &[ResolvedDiagnostics]) {
119 self.reporter.report(diagnostics);
121 }
122
123 fn register(&mut self, path: String, src: String) -> usize {
124 self.reporter.register(path, src)
126 }
127
128 fn buffer(&self) -> Option<String> {
129 self.reporter.buffer()
130 }
131}
132
133impl DiagnosticAssessor for Diagnostician {
134 fn assess(&self, d: &Diagnostic) -> Severity {
135 self.assessor.assess(d)
137 }
138
139 fn explain(&self, _error: &str) -> String {
140 unimplemented!("Error explanation is not supported on this diagnostian")
141 }
142
143 fn get_diagnostic_configuration(&self) -> String {
144 unimplemented!("Diagnostic configuration is not supported on this diagnostian")
145 }
146}
147
148#[allow(clippy::derivable_impls)]
150impl Default for Diagnostician {
151 fn default() -> Self {
152 Self {
153 reporter: Box::<CodeSpanDiagnosticReporter>::default(),
154 assessor: Box::<DiagnosticsRegistry>::default(),
155 filename_fileid_mapping: FxHashMap::default(),
156 }
157 }
158}
159
160pub trait DiagnosticAssessor {
164 fn assess(&self, d: &Diagnostic) -> Severity;
166 fn explain(&self, error: &str) -> String;
169 fn get_diagnostic_configuration(&self) -> String;
172}
173
174impl std::fmt::Display for Severity {
175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176 let severity = match self {
177 Severity::Error => "error",
178 Severity::Warning => "warning",
179 Severity::Info => "info",
180 Severity::Ignore => "ignore",
181 };
182 write!(f, "{severity}")
183 }
184}