Files
lang/src/io/mod.rs
T
2026-06-02 03:24:21 -04:00

142 lines
3.5 KiB
Rust

use std::path::{Path, PathBuf};
#[derive(Debug, Clone, Copy)]
pub struct Span {
pub file: usize,
pub start: usize,
pub end: usize,
}
pub struct Spanned<T> {
pub inner: T,
pub span: Span,
}
impl<T> std::ops::Deref for Spanned<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> std::ops::DerefMut for Spanned<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
pub struct CompilerMsg {
pub spans: Vec<Span>,
pub msg: String,
}
#[derive(Default)]
pub struct CompilerOutput {
pub errors: Vec<CompilerMsg>,
pub files: Vec<PathBuf>,
}
impl CompilerOutput {
pub fn new() -> Self {
Self::default()
}
pub fn error(&mut self, msg: impl Into<CompilerMsg>) {
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<String> 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<S: Into<String>> From<(S, Span)> for CompilerMsg {
fn from((msg, span): (S, Span)) -> Self {
Self {
spans: vec![span],
msg: msg.into(),
}
}
}