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, } 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) -> 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::>(); 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, ) -> std::ops::Range { 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, errors: Vec, } 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, 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::>(); let renderer = Renderer::styled().decor_style(annotate_snippets::renderer::DecorStyle::Unicode); renderer.render(&reports) } }