devalang_core/core/error/
mod.rs1use crate::core::parser::{
2 driver::Parser,
3 statement::{Statement, StatementKind},
4};
5use devalang_types::Value;
6use serde::{Deserialize, Serialize};
7
8pub struct ErrorHandler {
9 errors: Vec<Error>,
10}
11
12#[derive(Serialize, Deserialize, Clone, Debug)]
13pub enum Severity {
14 Warning,
15 Critical,
16}
17
18#[derive(Serialize, Deserialize, Clone, Debug, Default)]
19pub struct StackFrame {
20 pub module: Option<String>,
21 pub context: Option<String>,
22 pub line: usize,
23 pub column: usize,
24}
25
26#[derive(Serialize, Deserialize, Clone, Debug)]
27pub struct ErrorResult {
28 pub message: String,
29 pub line: usize,
30 pub column: usize,
31 pub severity: Severity,
32 pub stack: Vec<StackFrame>,
33}
34
35#[derive(Clone)]
36pub struct Error {
37 pub message: String,
38 pub line: usize,
39 pub column: usize,
40}
41
42impl Default for ErrorHandler {
43 fn default() -> Self {
44 Self::new()
45 }
46}
47
48impl ErrorHandler {
49 pub fn new() -> Self {
50 Self { errors: Vec::new() }
51 }
52
53 pub fn add_error(&mut self, message: String, line: usize, column: usize) {
54 let error_statement = Error {
55 message,
56 line,
57 column,
58 };
59 self.errors.push(error_statement);
60 }
61
62 pub fn has_errors(&self) -> bool {
63 !self.errors.is_empty()
64 }
65
66 pub fn get_errors(&self) -> &Vec<Error> {
67 &self.errors
68 }
69
70 pub fn clear_errors(&mut self) {
71 self.errors.clear();
72 }
73
74 pub fn detect_from_statements(&mut self, _parser: &mut Parser, statements: &[Statement]) {
75 for stmt in statements {
76 match &stmt.kind {
77 StatementKind::Unknown => {
78 self.add_error("Unknown statement".to_string(), stmt.line, stmt.column);
79 }
80 StatementKind::Error { message } => {
81 self.add_error(message.clone(), stmt.line, stmt.column);
82 }
83 _ => {}
84 }
85 }
86 }
87}
88
89pub fn collect_errors_recursively(statements: &[Statement]) -> Vec<ErrorResult> {
91 let mut errors: Vec<ErrorResult> = Vec::new();
92
93 for stmt in statements {
94 match &stmt.kind {
95 StatementKind::Unknown => {
96 errors.push(ErrorResult {
97 message: format!("Unknown statement at line {}:{}", stmt.line, stmt.column),
98 line: stmt.line,
99 column: stmt.column,
100 severity: Severity::Warning,
101 stack: vec![StackFrame {
102 module: None,
103 context: Some("Unknown".to_string()),
104 line: stmt.line,
105 column: stmt.column,
106 }],
107 });
108 }
109 StatementKind::Error { message } => {
110 errors.push(ErrorResult {
111 message: message.clone(),
112 line: stmt.line,
113 column: stmt.column,
114 severity: Severity::Critical,
115 stack: vec![StackFrame {
116 module: None,
117 context: Some("Error".to_string()),
118 line: stmt.line,
119 column: stmt.column,
120 }],
121 });
122 }
123 StatementKind::Loop => {
124 if let Some(body_statements) = extract_loop_body_statements(&stmt.value) {
125 let nested = collect_errors_recursively(body_statements);
126 errors.extend(nested.into_iter().map(|mut e| {
127 e.stack.insert(
128 0,
129 StackFrame {
130 module: None,
131 context: Some("loop".to_string()),
132 line: stmt.line,
133 column: stmt.column,
134 },
135 );
136 e
137 }));
138 }
139 }
140 _ => {}
141 }
142 }
143
144 errors
145}
146
147fn extract_loop_body_statements(value: &Value) -> Option<&[Statement]> {
148 if let Value::Map(map) = value {
149 if let Some(Value::Block(statements)) = map.get("body") {
150 return Some(statements);
151 }
152 }
153 None
154}
155
156pub fn partition_errors(errors: Vec<ErrorResult>) -> (Vec<ErrorResult>, Vec<ErrorResult>) {
157 let mut warnings = Vec::new();
158 let mut criticals = Vec::new();
159 for e in errors {
160 match e.severity {
161 Severity::Warning => warnings.push(e),
162 Severity::Critical => criticals.push(e),
163 }
164 }
165 (warnings, criticals)
166}
167
168pub fn log_errors_with_stack(prefix: &str, warnings: &[ErrorResult], criticals: &[ErrorResult]) {
169 use devalang_utils::logger::LogLevel;
170 use devalang_utils::logger::Logger;
171
172 let logger = Logger::new();
173 if !warnings.is_empty() {
174 logger.log_message(
175 LogLevel::Warning,
176 &format!("{}: {} warning(s)", prefix, warnings.len()),
177 );
178 for w in warnings {
179 logger.log_message(LogLevel::Warning, &format!("- {}", w.message));
180 if let Some(frame) = w.stack.first() {
181 let module = frame.module.clone().unwrap_or_default();
182 logger.log_message(
183 LogLevel::Debug,
184 &format!(
185 " ↳ {}:{}:{} {}",
186 module,
187 frame.line,
188 frame.column,
189 frame.context.clone().unwrap_or_default()
190 ),
191 );
192 }
193 if w.stack.len() > 1 {
194 for (i, f) in w.stack.iter().enumerate().skip(1) {
195 let module = f.module.clone().unwrap_or_default();
196 logger.log_message(
197 LogLevel::Debug,
198 &format!(
199 " #{} {}:{}:{} {}",
200 i,
201 module,
202 f.line,
203 f.column,
204 f.context.clone().unwrap_or_default()
205 ),
206 );
207 }
208 }
209 }
210 }
211 if !criticals.is_empty() {
212 logger.log_message(
213 LogLevel::Error,
214 &format!("{}: {} critical error(s)", prefix, criticals.len()),
215 );
216 for c in criticals {
217 logger.log_message(LogLevel::Error, &format!("- {}", c.message));
218 if let Some(frame) = c.stack.first() {
219 let module = frame.module.clone().unwrap_or_default();
220 logger.log_message(
221 LogLevel::Error,
222 &format!(
223 " ↳ {}:{}:{} {}",
224 module,
225 frame.line,
226 frame.column,
227 frame.context.clone().unwrap_or_default()
228 ),
229 );
230 }
231 if c.stack.len() > 1 {
232 for (i, f) in c.stack.iter().enumerate().skip(1) {
233 let module = f.module.clone().unwrap_or_default();
234 logger.log_message(
235 LogLevel::Error,
236 &format!(
237 " #{} {}:{}:{} {}",
238 i,
239 module,
240 f.line,
241 f.column,
242 f.context.clone().unwrap_or_default()
243 ),
244 );
245 }
246 }
247 }
248 }
249}
250
251pub fn collect_all_errors_with_modules(
253 statements_by_module: &std::collections::HashMap<String, Vec<Statement>>,
254) -> Vec<ErrorResult> {
255 let mut all: Vec<ErrorResult> = Vec::new();
256 for (module, stmts) in statements_by_module.iter() {
257 let mut errs = collect_errors_recursively(stmts);
258 for e in errs.iter_mut() {
259 if let Some(first) = e.stack.first_mut() {
261 if first.module.is_none() {
262 first.module = Some(module.clone());
263 }
264 }
265 }
266 all.extend(errs.into_iter());
267 }
268 all
269}