1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
use crate::{NyarError, NyarErrorKind};
use diagnostic::{Color, Diagnostic, FileID, FileSpan, ReportKind};
use std::{
    error::Error,
    fmt::{Debug, Display, Formatter},
    ops::Range,
};

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MissingError {
    pub(crate) kind: MissingErrorKind,
    pub(crate) span: FileSpan,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum MissingErrorKind {
    /// empty symbol
    EmptyPath,
    /// undefined symbol
    Undefined(Box<str>),
}

impl Error for MissingError {}

impl Display for MissingError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        Display::fmt(&self.kind, f)
    }
}

impl Display for MissingErrorKind {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::EmptyPath => write!(f, "Empty symbol"),
            Self::Undefined(v) => write!(f, "Undefined symbol `{}`", v),
        }
    }
}

impl From<MissingError> for NyarError {
    fn from(value: MissingError) -> Self {
        value.as_error(ReportKind::Error)
    }
}

impl MissingError {
    pub fn empty() -> Self {
        Self { kind: MissingErrorKind::EmptyPath, span: FileSpan::default() }
    }
    pub fn undefined(symbol: &str) -> Self {
        Self { kind: MissingErrorKind::Undefined(Box::from(symbol)), span: FileSpan::default() }
    }
    pub fn with_span(self, span: FileSpan) -> Self {
        Self { span, ..self }
    }
    pub fn with_range(self, range: Range<usize>) -> Self {
        Self { span: self.span.with_range(range), ..self }
    }
    pub fn with_file(self, file: FileID) -> Self {
        Self { span: self.span.with_file(file), ..self }
    }
    pub fn as_error(self, kind: ReportKind) -> NyarError {
        NyarErrorKind::Missing(self).as_error(kind)
    }
    pub fn as_report(&self, kind: ReportKind) -> Diagnostic {
        let mut report = Diagnostic::new(kind).with_location(self.span.get_file(), Some(self.span.get_start()));
        report.set_message(self.to_string());
        match self.kind {
            MissingErrorKind::EmptyPath => {
                report.add_label(self.span.as_label("Symbol path cannot be empty").with_color(Color::Red))
            }
            MissingErrorKind::Undefined(_) => {
                report.add_label(self.span.as_label("You need to declare this symbol by `let` first").with_color(Color::Red))
            }
        }
        report.finish()
    }
}