1use miette::{MietteError, MietteSpanContents, SourceCode, SourceSpan, SpanContents};
7
8mod diag;
9mod rc;
10mod src_ref;
11mod tree_display;
12
13pub type Id = compact_str::CompactString;
15
16pub const MICROCAD_EXTENSIONS: &[&str] = &["µcad", "mcad", "ucad"];
18
19pub use diag::{
20 Diag, DiagError, DiagHandler, DiagRenderOptions, DiagResult, Diagnostic, Level, PushDiag,
21};
22pub use rc::{Rc, RcMut};
23pub use src_ref::{Refer, SrcRef, SrcRefInner, SrcReferrer};
24pub use tree_display::{FormatTree, TreeDisplay, TreeState};
25
26pub struct MietteSourceFile<'a> {
28 source: &'a str,
29 name: String,
30 line_offset: usize,
31}
32
33impl MietteSourceFile<'static> {
34 pub fn invalid() -> Self {
36 MietteSourceFile {
37 source: crate::invalid_no_ansi!(FILE),
38 name: crate::invalid_no_ansi!(FILE).into(),
39 line_offset: 0,
40 }
41 }
42}
43
44impl SourceCode for MietteSourceFile<'_> {
45 fn read_span<'a>(
46 &'a self,
47 span: &SourceSpan,
48 context_lines_before: usize,
49 context_lines_after: usize,
50 ) -> Result<Box<dyn SpanContents<'a> + 'a>, MietteError> {
51 let inner_contents =
52 self.source
53 .read_span(span, context_lines_before, context_lines_after)?;
54 let contents = MietteSpanContents::new_named(
55 self.name.clone(),
56 inner_contents.data(),
57 *inner_contents.span(),
58 inner_contents.line() + self.line_offset,
59 inner_contents.column(),
60 inner_contents.line_count(),
61 )
62 .with_language("µcad");
63 Ok(Box::new(contents))
64 }
65}
66
67pub trait GetSourceStrByHash {
69 fn get_str_by_hash(&self, hash: u64) -> Option<&str>;
71
72 fn get_filename_by_hash(&self, hash: u64) -> Option<std::path::PathBuf>;
74}
75
76pub fn shorten(what: &str, max_chars: usize) -> String {
78 let short: String = what
79 .chars()
80 .enumerate()
81 .filter_map(|(p, ch)| {
82 if p == max_chars {
83 Some('…')
84 } else if p < max_chars {
85 if ch == '\n' { Some('⏎') } else { Some(ch) }
86 } else {
87 None
88 }
89 })
90 .collect();
91
92 if cfg!(feature = "ansi-color") && short.contains('\x1b') {
93 short + "\x1b[0m"
94 } else {
95 short
96 }
97}
98
99#[macro_export]
101macro_rules! shorten {
102 ($what:expr) => {
103 $crate::shorten(&format!("{}", $what), 140)
104 };
105 ($what:expr,$shorten:expr) => {
106 if $shorten {
107 $crate::shorten!($what)
108 } else {
109 $what
110 }
111 };
112 ($what:expr, $max_chars:literal) => {
113 shorten(format!("{}", $what).lines(), max_chars)
114 };
115}
116
117#[cfg(feature = "ansi-color")]
119#[macro_export]
120macro_rules! mark {
121 (FOUND!) => {
122 color_print::cformat!("<G!,k,s> FOUND </>")
123 };
124 (FOUND) => {
125 color_print::cformat!("<W!,k,s> FOUND </>")
126 };
127 (MATCH) => {
128 color_print::cformat!("<Y!,k,s> MATCH </>")
129 };
130 (NO_MATCH) => {
131 color_print::cformat!("<Y,k,s> NO MATCH </>")
132 };
133 (MATCH!) => {
134 color_print::cformat!("<G!,k,s> MATCH </>")
135 };
136 (NO_MATCH!) => {
137 color_print::cformat!("<R,k,s> NO MATCH </>")
138 };
139 (CALL) => {
140 color_print::cformat!("<B,k,s> CALL </>")
141 };
142 (LOOKUP) => {
143 color_print::cformat!("<c,s>LOOKUP</>")
144 };
145 (LOAD) => {
146 color_print::cformat!("<Y,k,s> LOADING </>")
147 };
148 (RESOLVE) => {
149 color_print::cformat!("<M,k,s> RESOLVE </>")
150 };
151 (AMBIGUOUS) => {
152 color_print::cformat!("<R,k,s> AMBIGUOUS </>")
153 };
154 (NOT_FOUND!) => {
155 color_print::cformat!("<R,k,s> NOT FOUND </>")
156 };
157 (NOT_FOUND) => {
158 color_print::cformat!("<Y,k,s> NOT FOUND </>")
159 };
160}
161
162#[cfg(not(feature = "ansi-color"))]
163#[macro_export]
164macro_rules! found {
165 (FOUND) => {
166 "Found"
167 };
168 (FINAL) => {
169 "Found"
170 };
171 (INTERMEDIATE) => {
172 "Found"
173 };
174 (MATCH) => {
175 "Match"
176 };
177 (NO_MATCH) => {
178 "No Match"
179 };
180 (CALL) => {
181 "Call"
182 };
183 (LOOKUP) => {
184 "Lookup"
185 };
186 (LOAD) => {
187 "Loading"
188 };
189 (RESOLVE) => {
190 "Resolve"
191 };
192 (AMBIGUOUS) => {
193 "Ambiguous"
194 };
195 (NOT_FOUND) => {
196 "Not found"
197 };
198 (NOT_FOUND) => {
199 "Not found"
200 };
201}
202
203#[cfg(feature = "ansi-color")]
205#[macro_export]
206macro_rules! invalid {
207 (VALUE) => {
208 color_print::cstr!("<R!,k,s> NO VALUE </>")
209 };
210 (TYPE) => {
211 color_print::cstr!("<R!,k,s> NO TYPE </>")
212 };
213 (OUTPUT) => {
214 color_print::cstr!("<R!,k,s> NO OUTPUT </>")
215 };
216 (STACK) => {
217 color_print::cstr!("<W,k,s> EMPTY STACK </>")
218 };
219 (REF) => {
220 color_print::cstr!("<Y!,k,s> NO REF </>")
221 };
222 (FILE) => {
223 color_print::cstr!("<Y!,k,s> NO FILE </>")
224 };
225 (RESULT) => {
226 color_print::cstr!("<Y!,k,s> NO RESULT </>")
227 };
228 (LINE) => {
229 color_print::cstr!("<Y!,k,s> NO LINE </>")
230 };
231 (SOURCE) => {
232 color_print::cstr!("<C!,k,s> FROM STR </>")
233 };
234 (UNKNOWN) => {
235 color_print::cstr!("<M!,k,s> UNKNOWN </>")
236 };
237 (ID) => {
238 color_print::cstr!("<M!,k,s> NO ID </>")
239 };
240 (NAME) => {
241 color_print::cstr!("<M!,k,s> NO NAME </>")
242 };
243 (EXPRESSION) => {
244 color_print::cstr!("<R!,k,s> INVALID EXPRESSION </>")
245 };
246}
247
248#[macro_export]
250macro_rules! invalid_no_ansi {
251 (VALUE) => {
252 "<NO VALUE>"
253 };
254 (TYPE) => {
255 "<NO TYPE>"
256 };
257 (OUTPUT) => {
258 "<NO OUTPUT>"
259 };
260 (STACK) => {
261 "<EMPTY STACK>"
262 };
263 (REF) => {
264 "<NO REF>"
265 };
266 (FILE) => {
267 "<NO FILE>"
268 };
269 (RESULT) => {
270 "<NO RESULT>"
271 };
272 (LINE) => {
273 "<NO LINE>"
274 };
275 (SOURCE) => {
276 "<FROM STR>"
277 };
278 (UNKNOWN) => {
279 "<UNKNOWN>"
280 };
281 (ID) => {
282 "<NO ID>"
283 };
284 (NAME) => {
285 "<INVALID NAME>"
286 };
287 (EXPRESSION) => {
288 "<INVALID EXPRESSION>"
289 };
290}
291
292#[macro_export]
293#[cfg(not(feature = "ansi-color"))]
294macro_rules! invalid {
295 ($x:literal) => {
296 invalid_no_ansi!($x)
297 };
298}
299
300pub trait WriteToFile: std::fmt::Display {
302 fn write_to_file(&self, filename: &impl AsRef<std::path::Path>) -> std::io::Result<()> {
304 use std::io::Write;
305 let file = std::fs::File::create(filename)?;
306 let mut writer = std::io::BufWriter::new(file);
307 write!(writer, "{self}")
308 }
309}