Skip to main content

microcad_lang_base/diag/
diagnostics.rs

1// Copyright © 2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use std::slice::Iter;
5
6use crate::{GetSourceLocInfoByHash, diag::*};
7
8/// µcad source diagnostics.
9#[derive(Debug, Default, Clone)]
10pub struct Diagnostics {
11    /// The number of overall errors in the evaluation process.
12    error_count: u32,
13    /// The number of overall warnings in the evaluation process.
14    warning_count: u32,
15    /// The list of diagnostics
16    diagnostics: Vec<Diagnostic>,
17}
18
19impl Diagnostics {
20    pub fn has_errors(&self) -> bool {
21        self.error_count > 0
22    }
23
24    pub fn has_warnings(&self) -> bool {
25        self.warning_count > 0
26    }
27
28    pub fn iter(&'_ self) -> Iter<'_, Diagnostic> {
29        self.diagnostics.iter()
30    }
31
32    /// Clear diagnostics.
33    pub fn clear(&mut self) {
34        self.diagnostics.clear();
35        self.error_count = 0;
36        self.warning_count = 0;
37    }
38
39    /// Pretty print this list of diagnostics.
40    pub fn pretty_print(
41        &self,
42        f: &mut dyn std::fmt::Write,
43        source_by_hash: &impl GetSourceLocInfoByHash,
44        options: &DiagRenderOptions,
45    ) -> std::fmt::Result {
46        self.diagnostics
47            .iter()
48            .try_for_each(|diag| diag.pretty_print(f, source_by_hash, options))
49    }
50
51    /// Merges another Diagnostics collection into this one.
52    pub fn append(&mut self, mut other: Diagnostics) {
53        self.error_count += other.error_count;
54        self.warning_count += other.warning_count;
55        self.diagnostics.append(&mut other.diagnostics);
56    }
57
58    /// Return overall number of occurred errors.
59    pub fn warning_count(&self) -> u32 {
60        self.warning_count
61    }
62
63    /// Return overall number of occurred errors.
64    pub fn error_count(&self) -> u32 {
65        self.error_count
66    }
67
68    /// return lines with errors
69    pub fn error_lines(&self) -> HashSet<u32> {
70        self.iter()
71            .filter_map(|d| {
72                if d.level() == Level::Error {
73                    d.src_ref().line()
74                } else {
75                    None
76                }
77            })
78            .collect()
79    }
80
81    /// return lines with warnings
82    pub fn warning_lines(&self) -> HashSet<u32> {
83        self.iter()
84            .filter_map(|d| {
85                if d.level() == Level::Warning {
86                    d.src_ref().line()
87                } else {
88                    None
89                }
90            })
91            .collect()
92    }
93}
94
95impl<R> From<R> for Diagnostics
96where
97    R: Into<Report>,
98{
99    fn from(report: R) -> Self {
100        Self {
101            error_count: 1,
102            warning_count: 0,
103            diagnostics: vec![Diagnostic::Error(std::rc::Rc::new(Refer::none(
104                report.into(),
105            )))],
106        }
107    }
108}
109
110impl FromIterator<Diagnostics> for Diagnostics {
111    fn from_iter<I: IntoIterator<Item = Diagnostics>>(iter: I) -> Self {
112        let mut root = Diagnostics::default();
113        for other in iter {
114            root.append(other);
115        }
116        root
117    }
118}
119
120impl PushDiag for Diagnostics {
121    fn push_diag(&mut self, diag: Diagnostic) -> DiagResult<()> {
122        match &diag {
123            Diagnostic::Error(_) => {
124                self.error_count += 1;
125            }
126            Diagnostic::Warning(_) => {
127                self.warning_count += 1;
128            }
129            _ => (),
130        }
131
132        self.diagnostics.push(diag);
133        Ok(())
134    }
135}