nate-derive 0.3.3

Proc-macros for NaTE
Documentation
use std::env::current_dir;
use std::fmt::{Display, Formatter};
use std::path::PathBuf;

use nom::Offset;

use crate::generate::SpanInput;

#[derive(Debug)]
pub(crate) enum CompileError {
    Nom(nom::Err<nom::error::Error<SpanInput>>),
    Darling(darling::Error),
    Syn(syn::Error),
    Lex(proc_macro::LexError),
    Fmt(std::fmt::Error),
    IoError(IoOp, PathBuf, std::io::Error),
}

#[derive(Debug, Clone, Copy)]
pub(crate) enum IoOp {
    Open,
    Metadata,
    Read,
    Write,
}

impl std::error::Error for CompileError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            CompileError::Nom(err) => Some(err),
            CompileError::Darling(err) => Some(err),
            CompileError::Syn(err) => Some(err),
            CompileError::Lex(err) => Some(err),
            CompileError::Fmt(err) => Some(err),
            CompileError::IoError(_, _, err) => Some(err),
        }
    }
}

impl From<nom::Err<nom::error::Error<SpanInput>>> for CompileError {
    fn from(err: nom::Err<nom::error::Error<SpanInput>>) -> Self {
        Self::Nom(err)
    }
}

impl From<darling::Error> for CompileError {
    fn from(err: darling::Error) -> Self {
        Self::Darling(err)
    }
}

impl From<syn::Error> for CompileError {
    fn from(err: syn::Error) -> Self {
        Self::Syn(err)
    }
}

impl From<proc_macro::LexError> for CompileError {
    fn from(err: proc_macro::LexError) -> Self {
        Self::Lex(err)
    }
}

impl From<std::fmt::Error> for CompileError {
    fn from(err: std::fmt::Error) -> Self {
        Self::Fmt(err)
    }
}

impl Display for CompileError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let err = match self {
            CompileError::Fmt(_) => return write!(f, "could not format generated code"),
            CompileError::Darling(err) => return write!(f, "{}", err),
            CompileError::Syn(err) => return write!(f, "{}", err),
            CompileError::Lex(err) => return write!(f, "could not parse input: {}", err),
            CompileError::IoError(op, path, err) => {
                let path = current_dir()
                    .ok()
                    .and_then(|cwd| path.strip_prefix(cwd).ok())
                    .unwrap_or(path);
                let op = match op {
                    IoOp::Open => "open",
                    IoOp::Metadata => "query",
                    IoOp::Read => "read from",
                    IoOp::Write => "write to",
                };
                return write!(f, "could not {} {:?}: {}", op, path, err);
            },
            CompileError::Nom(err) => err,
        };

        let input = match err {
            nom::Err::Incomplete(_) => unreachable!(),
            nom::Err::Error(err) | nom::Err::Failure(err) => &err.input,
        };
        let source = input.get_source();
        let path = input
            .get_shared()
            .as_deref()
            .and_then(std::path::Path::to_str)
            .unwrap_or("??");
        let row = input.location_line();
        let column = input.naive_get_utf8_column();

        let source_after = &source[source.offset(input)..];
        let source_after =
            if let Some((72, (i, _))) = source_after.char_indices().enumerate().take(73).last() {
                format!("{:?}...", &source_after[..i])
            } else {
                format!("{:?}", source_after)
            };

        write!(
            f,
            "Problems parsing template source {:?} at row {}, column {} near:\n{}",
            path, row, column, source_after,
        )
    }
}