meta_ast/interface/
report.rs1use crate::error::{Diagnostic, Error, Severity};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, clap::ValueEnum)]
11#[value(rename_all = "lower")]
12pub enum FailOn {
13 Never,
15 #[default]
17 Error,
18 Warning,
20}
21
22impl FailOn {
23 pub fn tripped(self, errors: usize, warnings: usize) -> bool {
25 match self {
26 FailOn::Never => false,
27 FailOn::Error => errors > 0,
28 FailOn::Warning => errors > 0 || warnings > 0,
29 }
30 }
31}
32
33pub fn report_diagnostics(diagnostics: &[Diagnostic], fail_on: FailOn) -> Result<(), Error> {
38 let mut errors = 0usize;
39 let mut warnings = 0usize;
40
41 for diagnostic in diagnostics {
42 let path = diagnostic.path.display().to_string();
43 let range = diagnostic
44 .source_range
45 .as_ref()
46 .map(|range| range.start.line + 1);
47 match diagnostic.severity {
48 Severity::Error => {
49 errors += 1;
50 tracing::error!(path = %path, line = ?range, "{}", diagnostic.message);
51 }
52 Severity::Warning => {
53 warnings += 1;
54 tracing::warn!(path = %path, line = ?range, "{}", diagnostic.message);
55 }
56 }
57 }
58
59 if fail_on.tripped(errors, warnings) {
60 return Err(Error::Diagnostics { errors, warnings });
61 }
62
63 Ok(())
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use std::path::PathBuf;
70
71 fn diagnostic(severity: Severity) -> Diagnostic {
72 Diagnostic {
73 path: PathBuf::from("a.py"),
74 severity,
75 message: "problem".into(),
76 source_range: None,
77 }
78 }
79
80 #[test]
81 fn never_accepts_errors() {
82 let diagnostics = [diagnostic(Severity::Error)];
83 assert!(report_diagnostics(&diagnostics, FailOn::Never).is_ok());
84 }
85
86 #[test]
87 fn error_policy_ignores_warnings() {
88 let diagnostics = [diagnostic(Severity::Warning)];
89 assert!(report_diagnostics(&diagnostics, FailOn::Error).is_ok());
90 }
91
92 #[test]
93 fn error_policy_fails_on_an_error() {
94 let diagnostics = [diagnostic(Severity::Warning), diagnostic(Severity::Error)];
95 let error = report_diagnostics(&diagnostics, FailOn::Error).unwrap_err();
96 assert!(matches!(
97 error,
98 Error::Diagnostics {
99 errors: 1,
100 warnings: 1
101 }
102 ));
103 }
104
105 #[test]
106 fn warning_policy_fails_on_a_warning() {
107 let diagnostics = [diagnostic(Severity::Warning)];
108 assert!(report_diagnostics(&diagnostics, FailOn::Warning).is_err());
109 }
110
111 #[test]
112 fn clean_run_never_fails() {
113 assert!(report_diagnostics(&[], FailOn::Warning).is_ok());
114 }
115}