use std::fmt;
use crate::{LineCol, Source, Span};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct YamlError {
pub diagnostic: Diagnostic,
}
impl YamlError {
#[must_use]
pub const fn new(diagnostic: Diagnostic) -> Self {
Self { diagnostic }
}
#[must_use]
pub fn with_position_from(mut self, source: &Source) -> Self {
if self.diagnostic.position.is_none() {
self.diagnostic.position = Some(source.diagnostic_position(&self.diagnostic));
}
self
}
}
impl fmt::Display for YamlError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.diagnostic.fmt(formatter)
}
}
impl std::error::Error for YamlError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Diagnostic {
pub kind: DiagnosticKind,
pub message: String,
pub span: Span,
pub position: Option<LineCol>,
pub expected: Vec<String>,
pub notes: Vec<String>,
}
impl Diagnostic {
#[must_use]
pub fn new(kind: DiagnosticKind, message: impl Into<String>, span: Span) -> Self {
Self {
kind,
message: message.into(),
span,
position: None,
expected: Vec::new(),
notes: Vec::new(),
}
}
#[must_use]
pub const fn with_position(mut self, position: LineCol) -> Self {
self.position = Some(position);
self
}
#[must_use]
pub fn with_expected(mut self, expected: impl Into<String>) -> Self {
self.expected.push(expected.into());
self
}
#[must_use]
pub fn with_note(mut self, note: impl Into<String>) -> Self {
self.notes.push(note.into());
self
}
}
impl fmt::Display for Diagnostic {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{:?}: {}", self.kind, self.message)?;
if let Some(position) = self.position {
write!(formatter, " at {}:{}", position.line, position.column)?;
}
if !self.expected.is_empty() {
write!(formatter, " (expected: {})", self.expected.join(", "))?;
}
for note in &self.notes {
write!(formatter, "\nnote: {note}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiagnosticKind {
Source,
Lexer,
Parser,
Semantic,
Typed,
Emitter,
}
pub type ParseError = YamlError;