microcad_lang/diag/
diagnostic.rs1use crate::{diag::*, resolve::*, src_ref::*};
5
6pub enum Diagnostic {
8 Trace(Refer<String>),
10 Info(Refer<String>),
12 Warning(Refer<Box<dyn std::error::Error>>),
14 Error(Refer<Box<dyn std::error::Error>>),
16}
17
18impl Diagnostic {
19 pub fn level(&self) -> Level {
21 match self {
22 Diagnostic::Trace(_) => Level::Trace,
23 Diagnostic::Info(_) => Level::Info,
24 Diagnostic::Warning(_) => Level::Warning,
25 Diagnostic::Error(_) => Level::Error,
26 }
27 }
28
29 pub fn message(&self) -> String {
31 match self {
32 Diagnostic::Trace(msg) | Diagnostic::Info(msg) => msg.to_string(),
33 Diagnostic::Warning(err) | Diagnostic::Error(err) => err.to_string(),
34 }
35 }
36
37 pub fn line(&self) -> Option<usize> {
39 let src_ref = match self {
40 Diagnostic::Trace(r) => r.src_ref(),
41 Diagnostic::Info(r) => r.src_ref(),
42 Diagnostic::Warning(r) => r.src_ref(),
43 Diagnostic::Error(r) => r.src_ref(),
44 };
45 src_ref.as_ref().map(|r| r.at.line)
46 }
47
48 pub fn pretty_print(
67 &self,
68 f: &mut dyn std::fmt::Write,
69 source_by_hash: &impl GetSourceByHash,
70 line_offset: usize,
71 ) -> std::fmt::Result {
72 let src_ref = self.src_ref();
73
74 let source_file = source_by_hash.get_by_hash(src_ref.source_hash());
75
76 fn make_relative(path: &std::path::Path) -> String {
77 let current_dir = std::env::current_dir().expect("current dir");
78 if let Ok(path) = path.canonicalize() {
79 pathdiff::diff_paths(path, current_dir)
80 .expect("related paths:\n {path:?}\n {current_dir:?}")
81 } else {
82 path.to_path_buf()
83 }
84 .to_string_lossy()
85 .to_string()
86 }
87 match &src_ref {
88 SrcRef(None) => writeln!(f, "{}: {}", self.level(), self.message())?,
89 SrcRef(Some(src_ref)) => {
90 writeln!(f, "{}: {}", self.level(), self.message())?;
91 writeln!(
92 f,
93 " ---> {}:{}",
94 source_file
95 .as_ref()
96 .map(|sf| make_relative(&sf.filename()))
97 .unwrap_or(crate::invalid_no_ansi!(FILE).to_string()),
98 src_ref.with_line_offset(line_offset).at
99 )?;
100 writeln!(f, " |",)?;
101
102 let line = source_file
103 .as_ref()
104 .map(|sf| {
105 sf.get_line(src_ref.at.line - 1)
106 .unwrap_or(crate::invalid!(LINE))
107 })
108 .unwrap_or(crate::invalid_no_ansi!(FILE));
109
110 writeln!(
111 f,
112 "{: >4} | {}",
113 src_ref.with_line_offset(line_offset).at.line,
114 line
115 )?;
116 writeln!(
117 f,
118 "{: >4} | {}",
119 "",
120 " ".repeat(src_ref.at.col - 1)
121 + &"^".repeat(src_ref.range.len().min(line.len())),
122 )?;
123 writeln!(f, " |",)?;
124 }
125 }
126
127 Ok(())
128 }
129}
130
131impl SrcReferrer for Diagnostic {
132 fn src_ref(&self) -> SrcRef {
133 match self {
134 Diagnostic::Trace(message) => message.src_ref(),
135 Diagnostic::Info(message) => message.src_ref(),
136 Diagnostic::Warning(error) => error.src_ref(),
137 Diagnostic::Error(error) => error.src_ref(),
138 }
139 }
140}
141
142impl std::fmt::Display for Diagnostic {
143 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
144 match self {
145 Diagnostic::Trace(message) => write!(f, "trace: {}: {message}", self.src_ref()),
146 Diagnostic::Info(message) => write!(f, "info: {}: {message}", self.src_ref()),
147 Diagnostic::Warning(error) => write!(f, "warning: {}: {error}", self.src_ref()),
148 Diagnostic::Error(error) => write!(f, "error: {}: {error}", self.src_ref()),
149 }
150 }
151}
152
153impl std::fmt::Debug for Diagnostic {
154 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
155 match self {
156 Diagnostic::Trace(message) => write!(f, "trace: {}: {message}", self.src_ref()),
157 Diagnostic::Info(message) => write!(f, "info: {}: {message}", self.src_ref()),
158 Diagnostic::Warning(error) => write!(f, "warning: {}: {error:?}", self.src_ref()),
159 Diagnostic::Error(error) => write!(f, "error: {}: {error:?}", self.src_ref()),
160 }
161 }
162}