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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use std::{
    error::Error,
    fmt::{Debug, Display, Formatter},
    ops::Range,
};

use diagnostic::{
    term::{
        emit,
        termcolor::{ColorChoice, StandardStream},
        TerminalConfig,
    },
    DiagnosticLevel, FileID, Span, TextStorage,
};

pub mod display;
mod error_io;
mod error_runtime;
mod error_syntax;

pub type QResult<T = ()> = Result<T, QError>;

pub type Validation<T> = diagnostic::Validation<T, QError>;

#[derive(Debug)]
pub struct QError {
    pub error: Box<QErrorKind>,
    pub level: DiagnosticLevel,
    pub source: Option<Box<dyn Error>>,
}

#[derive(Debug)]
pub enum QErrorKind {
    IO(IOError),
    Syntax(SyntaxError),
    Runtime(RuntimeError),
    Custom(String),
}

#[derive(Debug)]
pub struct SyntaxError {
    pub message: String,
    pub file: FileID,
    pub span: Span,
}

/// An error that occurs during runtime.
///
/// # Arguments
///
/// * `msg`:
///
/// returns: QError
///
/// # Examples
///
/// ```
/// use diagnostic_quick::RuntimeError;
/// RuntimeError::from("runtime error");
/// ```
#[derive(Debug)]
pub struct RuntimeError {
    pub message: String,
}

#[derive(Debug)]
pub struct IOError {
    pub message: String,
    pub file: FileID,
}

impl QError {
    pub fn syntax_error(msg: impl Into<String>) -> Self {
        let error = SyntaxError { message: msg.into(), file: Default::default(), span: Default::default() };
        Self { error: Box::new(QErrorKind::Syntax(error)), level: Default::default(), source: None }
    }
    pub fn runtime_error(msg: impl Into<String>) -> Self {
        let error = RuntimeError { message: msg.into() };
        Self { error: Box::new(QErrorKind::Runtime(error)), level: Default::default(), source: None }
    }
    pub fn kind(&self) -> &QErrorKind {
        &*self.error
    }
    pub fn with_file(mut self, file: &FileID) -> Self {
        match &mut *self.error {
            QErrorKind::IO(v) => {
                v.file = file.clone();
            }
            QErrorKind::Syntax(v) => {
                v.file = file.clone();
            }
            QErrorKind::Runtime(_) => {}
            QErrorKind::Custom(_) => {}
        }
        self
    }
    pub fn with_range(mut self, range: &Range<usize>) -> Self {
        match &mut *self.error {
            QErrorKind::IO(_) => {}
            QErrorKind::Syntax(v) => v.span = range.clone(),
            QErrorKind::Runtime(_) => {}
            QErrorKind::Custom(_) => {}
        }
        self
    }
    pub fn with_level(mut self, level: impl Into<DiagnosticLevel>) -> Self {
        self.level = level.into();
        self
    }
}

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

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

impl Error for QError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match &self.source {
            Some(s) => Some(&**s),
            None => None,
        }
    }
}