microcad_lang_base/diag/
diag_handler.rs1use crate::{GetSourceStrByHash, diag::*};
5use std::io::IsTerminal;
6
7#[derive(Default)]
9pub struct DiagHandler {
10 pub diag_list: DiagList,
12 error_count: u32,
14 warning_count: u32,
16 error_limit: Option<u32>,
19 error_limit_reached: bool,
21 warnings_as_errors: bool,
23 line_offset: usize,
25 pub render_options: DiagRenderOptions,
27}
28
29#[derive(Debug)]
31pub struct DiagRenderOptions {
32 pub color: bool,
34 pub unicode: bool,
36}
37
38impl Default for DiagRenderOptions {
39 fn default() -> Self {
40 DiagRenderOptions {
41 color: std::env::var("NO_COLOR").as_deref().unwrap_or("0") == "0",
42 unicode: std::io::stdout().is_terminal() && std::io::stderr().is_terminal(),
43 }
44 }
45}
46
47impl DiagRenderOptions {
48 pub fn theme(&self) -> miette::GraphicalTheme {
50 match (self.unicode, self.color) {
51 (true, true) => miette::GraphicalTheme::unicode(),
52 (true, false) => miette::GraphicalTheme::unicode_nocolor(),
53 (false, true) => miette::GraphicalTheme::ascii(),
54 (false, false) => miette::GraphicalTheme::none(),
55 }
56 }
57}
58
59impl DiagHandler {
61 pub fn new(line_offset: usize) -> Self {
63 Self {
64 line_offset,
65 ..Default::default()
66 }
67 }
68
69 pub fn pretty_print(
71 &self,
72 f: &mut dyn std::fmt::Write,
73 source_by_hash: &impl GetSourceStrByHash,
74 ) -> std::fmt::Result {
75 self.diag_list
76 .pretty_print(f, source_by_hash, self.line_offset, &self.render_options)
77 }
78
79 pub fn warning_count(&self) -> u32 {
81 self.warning_count
82 }
83
84 pub fn error_count(&self) -> u32 {
86 self.error_count
87 }
88
89 pub fn error_lines(&self) -> std::collections::HashSet<usize> {
91 self.diag_list
92 .iter()
93 .filter_map(|d| {
94 if d.level() == Level::Error {
95 d.line().map(|line| line + self.line_offset)
96 } else {
97 None
98 }
99 })
100 .collect()
101 }
102
103 pub fn warning_lines(&self) -> std::collections::HashSet<usize> {
105 self.diag_list
106 .iter()
107 .filter_map(|d| {
108 if d.level() == Level::Warning {
109 d.line().map(|line| line + self.line_offset)
110 } else {
111 None
112 }
113 })
114 .collect()
115 }
116
117 pub fn clear(&mut self) {
119 self.diag_list.clear();
120 self.error_count = 0;
121 self.warning_count = 0;
122 }
123}
124
125impl PushDiag for DiagHandler {
126 fn push_diag(&mut self, diag: Diagnostic) -> DiagResult<()> {
127 if let Some(error_limit) = self.error_limit {
128 if self.error_count >= error_limit && !self.error_limit_reached {
129 self.error(&SrcRef(None), DiagError::ErrorLimitReached(error_limit))?;
130 self.error_limit_reached = true;
131 }
132 return Err(DiagError::ErrorLimitReached(error_limit));
133 }
134
135 match &diag {
136 Diagnostic::Error(_) => {
137 self.error_count += 1;
138 }
139 Diagnostic::Warning(_) => {
140 if self.warnings_as_errors {
141 self.error_count += 1;
142 } else {
143 self.warning_count += 1;
144 }
145 }
146 _ => (),
147 }
148
149 self.diag_list.push_diag(diag)
150 }
151}