dinoco_compiler 1.2.8

The Dinoco schema compiler for parsing, validating, and analyzing database schemas.
Documentation
use std::fmt;

use crate::SourceOrigin;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RelatedDiagnostic {
    pub message: String,
    pub file: String,
    pub line: usize,
    pub column: usize,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompileError {
    pub message: String,
    pub file: Option<String>,
    pub line: usize,
    pub column: usize,
    pub related: Vec<RelatedDiagnostic>,
}

impl CompileError {
    pub(crate) fn new(message: impl Into<String>, line: usize, column: usize) -> Self {
        Self { message: message.into(), file: None, line, column, related: Vec::new() }
    }

    pub(crate) fn at(message: impl Into<String>, origin: &SourceOrigin) -> Self {
        Self {
            message: message.into(),
            file: Some(origin.file.clone()),
            line: origin.line,
            column: origin.column,
            related: Vec::new(),
        }
    }

    pub(crate) fn with_file(mut self, file: impl Into<String>) -> Self {
        self.file = Some(file.into());
        self
    }

    pub(crate) fn with_related(mut self, message: impl Into<String>, origin: &SourceOrigin) -> Self {
        self.related.push(RelatedDiagnostic {
            message: message.into(),
            file: origin.file.clone(),
            line: origin.line,
            column: origin.column,
        });
        self
    }
}

impl fmt::Display for CompileError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.message)?;
        if let Some(file) = &self.file {
            write!(f, "\n --> {file}:{}:{}", self.line, self.column)?;
        } else {
            write!(f, " at {}:{}", self.line, self.column)?;
        }
        for related in &self.related {
            write!(f, "\n  = {}\n --> {}:{}:{}", related.message, related.file, related.line, related.column)?;
        }
        Ok(())
    }
}

impl std::error::Error for CompileError {}

impl<R: pest::RuleType> From<pest::error::Error<R>> for CompileError {
    fn from(error: pest::error::Error<R>) -> Self {
        let (line, column) = match error.line_col {
            pest::error::LineColLocation::Pos((line, column)) => (line, column),
            pest::error::LineColLocation::Span((line, column), _) => (line, column),
        };

        Self::new(error.to_string(), line, column)
    }
}