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
19pub use diag_error::*;
20pub use diag_handler::*;
21pub use diag_list::*;
22pub use diagnostic::*;
23pub use level::*;
24
25use crate::src_ref::*;
26
27/// A trait to add diagnostics with different levels conveniently.
28pub trait PushDiag {
29    /// Push a diagnostic message (must be implemented).
30    fn push_diag(&mut self, diag: Diagnostic) -> DiagResult<()>;
31
32    /// Push new trace message.
33    fn trace(&mut self, src: &impl SrcReferrer, message: String) {
34        self.push_diag(Diagnostic::Trace(Refer::new(message, src.src_ref())))
35            .expect("could not push diagnostic trace message");
36    }
37    /// Push new informative message.
38    fn info(&mut self, src: &impl SrcReferrer, message: String) {
39        self.push_diag(Diagnostic::Info(Refer::new(message, src.src_ref())))
40            .expect("could not push diagnostic info message");
41    }
42    /// Push new warning.
43    fn warning(
44        &mut self,
45        src: &impl SrcReferrer,
46        err: impl std::error::Error + 'static,
47    ) -> DiagResult<()> {
48        let err = Diagnostic::Warning(Refer::new(err.into(), src.src_ref()));
49        if cfg!(feature = "ansi-color") {
50            log::warn!("{}", color_print::cformat!("<y,s>{err}</>"));
51        } else {
52            log::warn!("{err}");
53        }
54        self.push_diag(err)
55    }
56    /// Push new error.
57    fn error(
58        &mut self,
59        src: &impl SrcReferrer,
60        err: impl std::error::Error + 'static,
61    ) -> DiagResult<()> {
62        let err = Diagnostic::Error(Refer::new(err.into(), src.src_ref()));
63        if cfg!(feature = "ansi-color") {
64            log::error!("{}", color_print::cformat!("<r,s>{err}</>"));
65        } else {
66            log::error!("{err}");
67        }
68        self.push_diag(err)
69    }
70}
71
72/// Diagnosis trait gives access about collected errors.
73pub trait Diag {
74    /// Pretty print all errors.
75    fn fmt_diagnosis(&self, f: &mut dyn std::fmt::Write) -> std::fmt::Result;
76
77    /// Pretty write all errors into a file.
78    fn write_diagnosis(&self, w: &mut dyn std::io::Write) -> std::io::Result<()> {
79        write!(w, "{}", self.diagnosis())
80    }
81
82    /// Get pretty printed errors as string.
83    fn diagnosis(&self) -> String {
84        let mut str = String::new();
85        self.fmt_diagnosis(&mut str).expect("displayable diagnosis");
86        str
87    }
88
89    /// Returns true if there are warnings.
90    fn has_warnings(&self) -> bool {
91        self.warning_count() > 0
92    }
93
94    /// Return number of occurred warnings.
95    fn warning_count(&self) -> u32;
96
97    /// Returns true if there are errors.
98    fn has_errors(&self) -> bool {
99        self.error_count() > 0
100    }
101
102    /// Return number of occurred errors.
103    fn error_count(&self) -> u32;
104
105    /// Return all lines with errors
106    fn error_lines(&self) -> std::collections::HashSet<usize>;
107
108    /// Return all lines with warnings
109    fn warning_lines(&self) -> std::collections::HashSet<usize>;
110}
111
112/// Trait to write something with Display trait into a file.
113pub trait WriteToFile: std::fmt::Display {
114    /// Write something to a file.
115    fn write_to_file(&self, filename: &impl AsRef<std::path::Path>) -> std::io::Result<()> {
116        use std::io::Write;
117        let file = std::fs::File::create(filename)?;
118        let mut writer = std::io::BufWriter::new(file);
119        write!(writer, "{self}")
120    }
121}