microcad_lang/diag/
mod.rs

1// Copyright © 2024-2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Handling of diagnostic errors.
5//!
6//! While *evaluation* µcad is collecting [`Diagnostic`] messages.
7//!
8//! This is done in [`DiagHandler`] by providing the following traits:
9//!
10//! - [`PushDiag`]: Collects error in [`DiagHandler`]
11//! - [`Diag`]: Get diagnostic messages
12
13mod diag_error;
14mod diag_handler;
15mod diag_list;
16mod diagnostic;
17mod level;
18
19use miette::Report;
20pub use diag_error::*;
21pub use diag_handler::*;
22pub use diag_list::*;
23pub use diagnostic::*;
24pub use level::*;
25
26use crate::src_ref::*;
27
28/// A trait to add diagnostics with different levels conveniently.
29pub trait PushDiag {
30    /// Push a diagnostic message (must be implemented).
31    fn push_diag(&mut self, diag: Diagnostic) -> DiagResult<()>;
32
33    /// Push new trace message.
34    fn trace(&mut self, src: &impl SrcReferrer, message: String) {
35        self.push_diag(Diagnostic::Trace(Refer::new(Report::msg(message), src.src_ref())))
36            .expect("could not push diagnostic trace message");
37    }
38    /// Push new informative message.
39    fn info(&mut self, src: &impl SrcReferrer, message: String) {
40        self.push_diag(Diagnostic::Info(Refer::new(Report::msg(message), src.src_ref())))
41            .expect("could not push diagnostic info message");
42    }
43    /// Push new warning.
44    fn warning(
45        &mut self,
46        src: &impl SrcReferrer,
47        err: impl Into<Report>,
48    ) -> DiagResult<()> {
49        let err = Diagnostic::Warning(Refer::new(err.into(), src.src_ref()));
50        if cfg!(feature = "ansi-color") {
51            log::warn!("{}", color_print::cformat!("<y,s>{err}</>"));
52        } else {
53            log::warn!("{err}");
54        }
55        self.push_diag(err)
56    }
57    /// Push new error.
58    fn error(
59        &mut self,
60        src: &impl SrcReferrer,
61        err: impl Into<Report>,
62    ) -> DiagResult<()> {
63        let err = Diagnostic::Error(Refer::new(err.into(), src.src_ref()));
64        if cfg!(feature = "ansi-color") {
65            log::error!("{}", color_print::cformat!("<r,s>{err}</>"));
66        } else {
67            log::error!("{err}");
68        }
69        self.push_diag(err)
70    }
71}
72
73/// Diagnosis trait gives access about collected errors.
74pub trait Diag {
75    /// Pretty print all errors.
76    fn fmt_diagnosis(&self, f: &mut dyn std::fmt::Write) -> std::fmt::Result;
77
78    /// Pretty write all errors into a file.
79    fn write_diagnosis(&self, w: &mut dyn std::io::Write) -> std::io::Result<()> {
80        write!(w, "{}", self.diagnosis())
81    }
82
83    /// Get pretty printed errors as string.
84    fn diagnosis(&self) -> String {
85        let mut str = String::new();
86        self.fmt_diagnosis(&mut str).expect("displayable diagnosis");
87        str
88    }
89
90    /// Returns true if there are warnings.
91    fn has_warnings(&self) -> bool {
92        self.warning_count() > 0
93    }
94
95    /// Return number of occurred warnings.
96    fn warning_count(&self) -> u32;
97
98    /// Returns true if there are errors.
99    fn has_errors(&self) -> bool {
100        self.error_count() > 0
101    }
102
103    /// Return number of occurred errors.
104    fn error_count(&self) -> u32;
105
106    /// Return all lines with errors
107    fn error_lines(&self) -> std::collections::HashSet<usize>;
108
109    /// Return all lines with warnings
110    fn warning_lines(&self) -> std::collections::HashSet<usize>;
111}
112
113/// Trait to write something with Display trait into a file.
114pub trait WriteToFile: std::fmt::Display {
115    /// Write something to a file.
116    fn write_to_file(&self, filename: &impl AsRef<std::path::Path>) -> std::io::Result<()> {
117        use std::io::Write;
118        let file = std::fs::File::create(filename)?;
119        let mut writer = std::io::BufWriter::new(file);
120        write!(writer, "{self}")
121    }
122}