use std::path::{Path, PathBuf}; #[derive(Debug, Clone, Copy)] pub struct Span { pub file: usize, pub start: usize, pub end: usize, } pub struct Spanned { pub inner: T, pub span: Span, } impl std::ops::Deref for Spanned { type Target = T; fn deref(&self) -> &Self::Target { &self.inner } } impl std::ops::DerefMut for Spanned { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } pub struct CompilerMsg { pub spans: Vec, pub msg: String, } #[derive(Default)] pub struct CompilerOutput { pub errors: Vec, pub files: Vec, } impl CompilerOutput { pub fn new() -> Self { Self::default() } pub fn error(&mut self, msg: impl Into) { self.errors.push(msg.into()); } pub fn write(&self, w: &mut impl std::io::Write) { let files: Vec<_> = self .files .iter() .map(|path| std::fs::read_to_string(path).unwrap()) .collect(); for error in &self.errors { writeln!(w, "Error: {}", error.msg).unwrap(); for span in &error.spans { span.write(w, &files[span.file]).unwrap(); } } } } impl Span { pub fn write(&self, w: &mut impl std::io::Write, text: &str) -> std::io::Result<()> { let mut line_start = 0; let mut found = false; let mut line = 1; let mut spans = Vec::new(); for (i, c) in text.char_indices() { if i == self.start { found = true; } if i == self.end { found = true; } if c == '\n' { if found { spans.push((line, line_start..i)); } line_start = i + 1; line += 1; found = false; } } let underline = "\x1b[4:3m"; let underline_color = "\x1b[58;5;1m"; let end = "\x1b[0m"; if let [(line, range)] = &spans[..] { writeln!( w, " {line:3} | {}{underline}{underline_color}{}{end}{}", &text[range.start..self.start], &text[self.start..=self.end], &text[(self.end + 1)..range.end] )?; } else if let [(sline, srange), (eline, erange)] = &spans[..] { writeln!( w, " {sline:3} | {}{underline}{underline_color}{}{end}", &text[srange.start..self.start], &text[self.start..=srange.end - 1], )?; if *eline != *sline + 1 { writeln!(w, " ...")?; } writeln!( w, " {eline:3} | {underline}{underline_color}{}{end}{}", &text[erange.start..=self.end], &text[(self.end + 1)..=erange.end - 1], )?; } Ok(()) } } impl From for CompilerMsg { fn from(msg: String) -> Self { Self { spans: Vec::new(), msg, } } } impl From<&str> for CompilerMsg { fn from(msg: &str) -> Self { Self { spans: Vec::new(), msg: msg.to_string(), } } } impl> From<(S, Span)> for CompilerMsg { fn from((msg, span): (S, Span)) -> Self { Self { spans: vec![span], msg: msg.into(), } } }