hax_types/diagnostics/
report.rs1use super::Diagnostics;
2use annotate_snippets::*;
3use miette::SourceOffset;
4use std::collections::HashMap;
5use std::path::{Path, PathBuf};
6use std::rc::Rc;
7
8#[derive(Clone, Debug, Default)]
10pub struct ReportCtx {
11 files: HashMap<PathBuf, Rc<String>>,
12}
13
14fn compute_offset(src: &str, line: usize, col: usize) -> usize {
16 SourceOffset::from_location(src, line, col).offset() + 1
17}
18
19impl ReportCtx {
20 fn file_contents<'a>(&'a mut self, path: PathBuf) -> Rc<String> {
22 self.files
23 .entry(path.clone())
24 .or_insert_with(|| {
25 let s =
26 std::fs::read_to_string(&path).expect(&format!("Unable to read file {path:?}"));
27 Rc::new(s)
28 })
29 .clone()
30 }
31}
32
33impl Diagnostics {
34 pub fn with_message<R, F: for<'a> FnMut(Message<'a>) -> R>(
37 &self,
38 report_ctx: &mut ReportCtx,
39 working_dir: &Path,
40 level: Level,
41 mut then: F,
42 ) -> R {
43 let mut snippets_data = vec![];
44
45 for span in &self.span {
46 if let Some(path) = span.filename.to_path() {
47 let source = {
48 let mut path = path.to_path_buf();
49 if path.is_relative() {
50 path = working_dir.join(&path);
51 };
52 report_ctx.file_contents(path)
53 };
54 let start = compute_offset(&source, span.lo.line, span.lo.col);
55 let end = compute_offset(&source, span.hi.line, span.hi.col);
56 let origin = format!("{}", path.display());
57 snippets_data.push((source, origin, start..end));
58 };
59 }
60
61 let title = format!("[{}] {self}", self.kind.code());
62 let message =
63 level
64 .title(&title)
65 .snippets(snippets_data.iter().map(|(source, origin, range)| {
66 Snippet::source(source)
67 .line_start(1)
68 .origin(&origin)
69 .fold(true)
70 .annotation(level.span(range.clone()))
71 }));
72
73 then(message)
74 }
75}