1use std::sync::Arc;
2
3use ariadne::{Color, Label, Report, ReportKind, Source};
4
5use crate::utils::source::SourceFile;
6use crate::utils::span::Span;
7
8#[derive(Debug, Clone)]
10struct ErrorDetail {
11 primary: (Span, String),
13 labels: Vec<(Span, String)>,
15 source: Option<Arc<String>>,
17 source_name: Option<String>,
19 help: Option<String>,
21}
22
23#[derive(Debug, Clone)]
25pub struct Error {
26 message: String,
28 line: Option<usize>,
30 reason: Option<ErrorReason>,
32 detail: Option<Box<ErrorDetail>>,
34}
35
36#[derive(Debug, Clone)]
38pub struct ErrorReason {
39 error_type: Reason,
41 data: Option<Vec<String>>,
43}
44
45#[derive(Clone, Copy, Debug)]
47pub enum Reason {
48 Parse,
50 AST,
52 Lexer,
54 Interpreter,
56 Utils,
58 Compile,
60 Runtime,
62}
63
64impl Error {
65 pub fn at(kind: Reason, message: impl Into<String>, span: Span) -> Self {
68 let message = message.into();
69 #[cfg(feature = "debug")]
70 log::debug!("Error: {}", message);
71 Self {
72 message: message.clone(),
73 line: None,
74 reason: Some(ErrorReason::init(kind, None)),
75 detail: Some(Box::new(ErrorDetail {
76 primary: (span, message),
77 labels: Vec::new(),
78 source: None,
79 source_name: None,
80 help: None,
81 })),
82 }
83 }
84
85 pub fn with_primary_label(mut self, label: impl Into<String>) -> Self {
87 if let Some(d) = &mut self.detail {
88 d.primary.1 = label.into();
89 }
90 self
91 }
92
93 pub fn with_label(mut self, span: Span, label: impl Into<String>) -> Self {
95 if let Some(d) = &mut self.detail {
96 d.labels.push((span, label.into()));
97 }
98 self
99 }
100
101 pub fn with_source(mut self, source: Arc<String>) -> Self {
103 if let Some(d) = &mut self.detail {
104 d.source = Some(source);
105 }
106 self
107 }
108
109 pub fn with_source_name(mut self, name: impl Into<String>) -> Self {
111 if let Some(d) = &mut self.detail {
112 d.source_name = Some(name.into());
113 }
114 self
115 }
116
117 pub fn with_help(mut self, help: impl Into<String>) -> Self {
119 if let Some(d) = &mut self.detail {
120 d.help = Some(help.into());
121 }
122 self
123 }
124
125 pub fn with_source_file(mut self, file: &SourceFile) -> Self {
127 if let Some(d) = &mut self.detail {
128 d.source = Some(Arc::clone(&file.text));
129 d.source_name = Some(file.name.to_string());
130 }
131 self
132 }
133
134 pub fn print_error(&self) {
139 self.report_to_stderr();
140 panic!("rl error");
141 }
142
143 pub fn report_to_stderr(&self) {
146 if let Some(d) = &self.detail
147 && let Some(src) = &d.source
148 {
149 let name: &str = d.source_name.as_deref().unwrap_or("<source>");
150 let (sp, primary_label) = &d.primary;
151 let mut builder = Report::build(ReportKind::Error, (name, sp.start..sp.end))
152 .with_message(&self.message)
153 .with_label(
154 Label::new((name, sp.start..sp.end))
155 .with_message(primary_label)
156 .with_color(Color::Red),
157 );
158 for (lsp, label) in &d.labels {
159 builder = builder.with_label(
160 Label::new((name, lsp.start..lsp.end))
161 .with_message(label)
162 .with_color(Color::Yellow),
163 );
164 }
165 if let Some(help) = &d.help {
166 builder = builder.with_help(help);
167 }
168 let _ = builder.finish().eprint((name, Source::from(src.as_str())));
169 return;
170 }
171
172 self.fallback_text();
173 }
174
175 fn fallback_text(&self) {
177 match &self.line {
178 Some(l) => println!("[{}) Error: {}]", l, self.message),
179 None => println!("[Error: {}]", self.message),
180 }
181
182 if let Some(r) = &self.reason {
183 match &r.data {
184 Some(d) => {
185 println!("[{}]", r.get_type_string());
186 for l in d {
187 println!("{}", l);
188 }
189 }
190 _ => println!("[{}]", r.get_type_string()),
191 }
192 }
193 }
194
195 pub fn span(&self) -> Option<crate::utils::span::Span> {
197 self.detail.as_ref().map(|d| d.primary.0)
198 }
199}
200
201impl ErrorReason {
202 pub fn init(error_type: Reason, data: Option<Vec<String>>) -> Self {
211 Self { error_type, data }
212 }
213
214 fn get_type_string(&self) -> String {
216 match &self.error_type {
217 Reason::Parse => "Parse Error",
218 Reason::AST => "AST Error",
219 Reason::Lexer => "Lexer Error",
220 Reason::Interpreter => "Interpreter Error",
221 Reason::Utils => "Utils Error",
222 Reason::Compile => "Compile Error",
223 Reason::Runtime => "Runtime Error",
224 }
225 .to_string()
226 }
227}
228
229impl Error {
230 pub fn message(&self) -> &str {
232 &self.message
233 }
234}