tracing_filter/
diagnostics.rs1use miette::{GraphicalReportHandler, GraphicalTheme};
2
3use {
4 miette::{Diagnostic, ReportHandler},
5 std::{borrow::Cow, error::Error, fmt},
6};
7
8pub struct Diagnostics<'a> {
10 pub(crate) error: Option<Box<dyn Diagnostic + Send + Sync + 'static>>,
11 pub(crate) ignored: Vec<Box<dyn Diagnostic + Send + Sync + 'static>>,
12 pub(crate) disabled: Option<Box<dyn Diagnostic + Send + Sync + 'static>>,
13 pub(crate) source: Cow<'a, str>,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub enum DiagnosticsTheme {
19 Ascii,
21 AsciiNocolor,
23 Unicode,
25 UnicodeNocolor,
27 Guess,
29}
30
31impl Default for DiagnosticsTheme {
32 fn default() -> Self {
33 DiagnosticsTheme::Guess
34 }
35}
36
37impl DiagnosticsTheme {
38 fn report_handler(self) -> GraphicalReportHandler {
39 match self {
40 Self::Ascii => GraphicalReportHandler::new_themed(GraphicalTheme::ascii()),
41 Self::AsciiNocolor => GraphicalReportHandler::new_themed(GraphicalTheme::none()),
42 Self::Unicode => GraphicalReportHandler::new_themed(GraphicalTheme::unicode()),
43 Self::UnicodeNocolor => {
44 GraphicalReportHandler::new_themed(GraphicalTheme::unicode_nocolor())
45 },
46 Self::Guess => GraphicalReportHandler::new(),
47 }
48 }
49}
50
51impl Diagnostics<'_> {
52 pub fn is_error(&self) -> bool {
54 self.error.is_some()
55 }
56
57 pub fn is_warning(&self) -> bool {
59 !self.ignored.is_empty() || self.disabled.is_some()
60 }
61
62 pub fn is_empty(&self) -> bool {
64 !self.is_error() && !self.is_warning()
65 }
66
67 pub fn into_owned(self) -> Diagnostics<'static> {
70 Diagnostics {
71 error: self.error,
72 ignored: self.ignored,
73 disabled: self.disabled,
74 source: Cow::Owned(self.source.into()),
75 }
76 }
77
78 pub fn error(&self, theme: DiagnosticsTheme) -> Option<impl fmt::Display + '_> {
82 struct ErrorDiagnostics<'a>(&'a Diagnostics<'a>, DiagnosticsTheme);
83 impl fmt::Display for ErrorDiagnostics<'_> {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 let report_handler = self.1.report_handler();
86 if let Some(error) = &self.0.error {
87 report_handler.debug(&DiagnosticWithStr::new(&**error, &self.0.source), f)?;
88 }
89 Ok(())
90 }
91 }
92 if self.is_error() {
93 Some(ErrorDiagnostics(self, theme))
94 } else {
95 None
96 }
97 }
98
99 pub fn warn(&self, theme: DiagnosticsTheme) -> Option<impl fmt::Display + '_> {
103 struct WarnDiagnostics<'a>(&'a Diagnostics<'a>, DiagnosticsTheme);
104 impl fmt::Display for WarnDiagnostics<'_> {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 let report_handler = self.1.report_handler();
107
108 if !self.0.ignored.is_empty() {
109 writeln!(
110 f,
111 "{} directives were ignored as invalid",
112 self.0.ignored.len()
113 )?;
114 }
115
116 for ignored in &self.0.ignored {
117 report_handler.debug(&DiagnosticWithStr::new(&**ignored, &self.0.source), f)?;
118 }
119
120 if let Some(disabled) = &self.0.disabled {
121 report_handler
122 .debug(&DiagnosticWithStr::new(&**disabled, &self.0.source), f)?;
123 }
124
125 Ok(())
126 }
127 }
128 if self.is_warning() {
129 Some(WarnDiagnostics(self, theme))
130 } else {
131 None
132 }
133 }
134}
135
136impl fmt::Display for Diagnostics<'_> {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 if f.alternate() {
139 f.debug_struct(stringify!(Diagnostics))
140 .field(stringify!(error), &self.error)
141 .field(stringify!(ignored), &self.ignored)
142 .field(stringify!(disabled), &self.disabled)
143 .finish()
144 } else {
145 if let Some(error) = &self.error {
146 writeln!(f, "{}", error)?;
147 }
148
149 if !self.ignored.is_empty() {
150 writeln!(
151 f,
152 "{} directives were ignored as invalid",
153 self.ignored.len()
154 )?;
155 for ignored in &self.ignored {
156 writeln!(f, "{}", ignored)?;
157 }
158 }
159
160 if let Some(disabled) = &self.disabled {
161 writeln!(f, "{}", disabled)?;
162 }
163
164 Ok(())
165 }
166 }
167}
168
169impl fmt::Debug for Diagnostics<'_> {
170 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171 if f.alternate() {
172 f.debug_struct(stringify!(Diagnostics))
173 .field(stringify!(error), &self.error)
174 .field(stringify!(ignored), &self.ignored)
175 .field(stringify!(disabled), &self.disabled)
176 .finish()
177 } else {
178 if let Some(error) = self.error(DiagnosticsTheme::Guess) {
179 writeln!(f, "{}", error)?;
180 }
181 if let Some(warn) = self.warn(DiagnosticsTheme::Guess) {
182 writeln!(f, "{}", warn)?;
183 }
184 Ok(())
185 }
186 }
187}
188
189#[derive(Debug)]
190struct DiagnosticWithStr<'a> {
191 diagnostic: &'a dyn Diagnostic,
192 source: &'a str,
193}
194
195impl<'a> DiagnosticWithStr<'a> {
196 fn new(diagnostic: &'a dyn Diagnostic, source: &'a str) -> Self {
197 Self { diagnostic, source }
198 }
199}
200
201impl fmt::Display for DiagnosticWithStr<'_> {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 fmt::Display::fmt(&self.diagnostic, f)
204 }
205}
206
207impl Error for DiagnosticWithStr<'_> {}
208
209impl Diagnostic for DiagnosticWithStr<'_> {
210 fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
211 self.diagnostic.code()
212 }
213
214 fn severity(&self) -> Option<miette::Severity> {
215 self.diagnostic.severity()
216 }
217
218 fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
219 self.diagnostic.help()
220 }
221
222 fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
223 self.diagnostic.url()
224 }
225
226 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
227 Some(&self.source)
228 }
229
230 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
231 self.diagnostic.labels()
232 }
233
234 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
235 self.diagnostic.related()
236 }
237}