nomo/src/errors.rs
Marcel Müller 7f7bf5c98d Start fixing error outputs
Signed-off-by: Marcel Müller <neikos@neikos.email>
2026-03-16 11:22:29 +01:00

126 lines
3.8 KiB
Rust

use std::sync::Arc;
use annotate_snippets::AnnotationKind;
use annotate_snippets::Level;
use annotate_snippets::Renderer;
use annotate_snippets::Snippet;
use thiserror::Error;
use winnow::stream::Offset;
use crate::input::NomoInput;
use crate::lexer::ParseError;
use crate::parser::AstError;
/// An error occurred while producing an Ast
#[derive(Debug, Error)]
pub struct AstFailure {
errors: Vec<AstError>,
}
impl std::fmt::Display for AstFailure {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("TODO")
}
}
impl AstFailure {
pub(crate) fn from_errors(errors: Vec<AstError>) -> AstFailure {
AstFailure { errors }
}
/// Create a CLI printable report
pub fn to_report(&self, source: &str) -> String {
let reports = self
.errors
.iter()
.map(|error| {
annotate_snippets::Level::ERROR
.primary_title(
error
.message
.as_deref()
.unwrap_or("An error occurred while producing an Ast"),
)
.element(annotate_snippets::Snippet::source(source).annotation(
annotate_snippets::AnnotationKind::Primary.span(
constrain_without_whitespace(
source,
error.span.clone().map(|s| s.range).unwrap_or_else(|| 0..0),
),
),
))
.elements(
error
.help
.as_ref()
.map(|help| annotate_snippets::Level::HELP.message(help)),
)
})
.collect::<Vec<_>>();
let renderer = annotate_snippets::Renderer::styled()
.decor_style(annotate_snippets::renderer::DecorStyle::Unicode);
renderer.render(&reports)
}
}
fn constrain_without_whitespace(
input: &str,
range: std::ops::Range<usize>,
) -> std::ops::Range<usize> {
let trimmed = input[range].trim();
let start = trimmed.offset_from(&input);
let end = start + trimmed.len();
start..end
}
/// An error occurred during lexing
#[derive(Debug, Error)]
pub struct ParseFailure {
input: Arc<str>,
errors: Vec<ParseError>,
}
impl std::fmt::Display for ParseFailure {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.to_report())
}
}
impl ParseFailure {
pub(crate) fn from_errors(errors: Vec<ParseError>, input: NomoInput) -> ParseFailure {
ParseFailure {
input: Arc::from(input.to_string()),
errors,
}
}
/// Produce a CLi printable report
pub fn to_report(&self) -> String {
let reports = self
.errors
.iter()
.map(|error| {
Level::ERROR
.primary_title(
error
.message
.as_deref()
.unwrap_or("An error occurred while parsing"),
)
.element(
Snippet::source(self.input.as_ref()).annotation(
AnnotationKind::Primary
.span(error.span.clone().map(|s| s.range).unwrap_or_else(|| 0..0)),
),
)
.elements(error.help.as_ref().map(|help| Level::HELP.message(help)))
})
.collect::<Vec<_>>();
let renderer =
Renderer::styled().decor_style(annotate_snippets::renderer::DecorStyle::Unicode);
renderer.render(&reports)
}
}