Skip to content

Commit f15c7f1

Browse files
authored
feat(core): better error handling + formatting
feat(core): better error handling + formatting
2 parents a1467ed + 18434f9 commit f15c7f1

File tree

12 files changed

+595
-448
lines changed

12 files changed

+595
-448
lines changed

.github/workflows/release.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ on:
44
workflow_dispatch:
55
push:
66
branches: [main]
7-
tags:
8-
- "v*"
97

108
jobs:
119
build:

core/src/error.rs

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
use miette as oxc_miette;
6+
use owo_colors::OwoColorize;
7+
use oxc_diagnostics::OxcDiagnostic;
8+
use oxc_miette::{Diagnostic, NamedSource, SourceSpan};
9+
use std::fmt;
10+
11+
/// Comprehensive error type for Andromeda runtime operations
12+
#[derive(Diagnostic, Debug, Clone)]
13+
pub enum AndromedaError {
14+
/// File system operation errors
15+
#[diagnostic(
16+
code(andromeda::fs::io_error),
17+
help("Check that the file exists and you have proper permissions")
18+
)]
19+
FsError {
20+
operation: String,
21+
path: String,
22+
error_message: String,
23+
},
24+
25+
/// Parse errors from JavaScript/TypeScript source
26+
#[diagnostic(
27+
code(andromeda::parse::syntax_error),
28+
help("Check the syntax of your JavaScript/TypeScript code")
29+
)]
30+
ParseError {
31+
errors: Vec<OxcDiagnostic>,
32+
source_path: String,
33+
#[source_code]
34+
source_code: NamedSource<String>,
35+
},
36+
37+
/// Runtime execution errors
38+
#[diagnostic(
39+
code(andromeda::runtime::execution_error),
40+
help("Check the runtime state and ensure all required resources are available")
41+
)]
42+
RuntimeError {
43+
message: String,
44+
#[label("error occurred here")]
45+
location: Option<SourceSpan>,
46+
#[source_code]
47+
source_code: Option<NamedSource<String>>,
48+
},
49+
50+
/// Extension loading errors
51+
#[diagnostic(
52+
code(andromeda::extension::load_error),
53+
help("Ensure the extension is properly configured and dependencies are available")
54+
)]
55+
ExtensionError {
56+
extension_name: String,
57+
message: String,
58+
},
59+
60+
/// Resource management errors
61+
#[diagnostic(
62+
code(andromeda::resource::invalid_rid),
63+
help("Ensure the resource ID is valid and the resource hasn't been closed")
64+
)]
65+
ResourceError {
66+
rid: u32,
67+
operation: String,
68+
message: String,
69+
},
70+
71+
/// Task management errors
72+
#[diagnostic(
73+
code(andromeda::task::task_error),
74+
help("Check task state and ensure proper cleanup")
75+
)]
76+
TaskError { task_id: u32, message: String },
77+
/// Network/HTTP errors
78+
#[diagnostic(
79+
code(andromeda::network::request_error),
80+
help("Check network connectivity and request parameters")
81+
)]
82+
NetworkError {
83+
url: String,
84+
operation: String,
85+
error_message: String,
86+
},
87+
88+
/// Encoding/Decoding errors
89+
#[diagnostic(
90+
code(andromeda::encoding::decode_error),
91+
help("Ensure the input data is properly formatted")
92+
)]
93+
EncodingError { format: String, message: String },
94+
95+
/// Configuration errors
96+
#[diagnostic(
97+
code(andromeda::config::invalid_config),
98+
help("Check the configuration file format and required fields")
99+
)]
100+
ConfigError { field: String, message: String },
101+
}
102+
103+
impl AndromedaError {
104+
/// Create a new file system error
105+
pub fn fs_error(
106+
source: std::io::Error,
107+
operation: impl Into<String>,
108+
path: impl Into<String>,
109+
) -> Self {
110+
Self::FsError {
111+
error_message: source.to_string(),
112+
operation: operation.into(),
113+
path: path.into(),
114+
}
115+
}
116+
117+
/// Create a new parse error with enhanced diagnostics
118+
pub fn parse_error(
119+
errors: Vec<OxcDiagnostic>,
120+
source_path: impl Into<String>,
121+
source_code: impl Into<String>,
122+
) -> Self {
123+
let source_path = source_path.into();
124+
let source_code = source_code.into();
125+
Self::ParseError {
126+
errors,
127+
source_path: source_path.clone(),
128+
source_code: NamedSource::new(source_path, source_code),
129+
}
130+
}
131+
132+
/// Create a new runtime error
133+
pub fn runtime_error(message: impl Into<String>) -> Self {
134+
Self::RuntimeError {
135+
message: message.into(),
136+
location: None,
137+
source_code: None,
138+
}
139+
}
140+
141+
/// Create a new runtime error with source location
142+
pub fn runtime_error_with_location(
143+
message: impl Into<String>,
144+
source_code: impl Into<String>,
145+
source_path: impl Into<String>,
146+
location: SourceSpan,
147+
) -> Self {
148+
Self::RuntimeError {
149+
message: message.into(),
150+
location: Some(location),
151+
source_code: Some(NamedSource::new(source_path.into(), source_code.into())),
152+
}
153+
}
154+
155+
/// Create a new extension error
156+
pub fn extension_error(extension_name: impl Into<String>, message: impl Into<String>) -> Self {
157+
Self::ExtensionError {
158+
extension_name: extension_name.into(),
159+
message: message.into(),
160+
}
161+
}
162+
163+
/// Create a new resource error
164+
pub fn resource_error(
165+
rid: u32,
166+
operation: impl Into<String>,
167+
message: impl Into<String>,
168+
) -> Self {
169+
Self::ResourceError {
170+
rid,
171+
operation: operation.into(),
172+
message: message.into(),
173+
}
174+
}
175+
176+
/// Create a new task error
177+
pub fn task_error(task_id: u32, message: impl Into<String>) -> Self {
178+
Self::TaskError {
179+
task_id,
180+
message: message.into(),
181+
}
182+
}
183+
/// Create a new network error
184+
pub fn network_error(
185+
source: Box<dyn std::error::Error + Send + Sync>,
186+
url: impl Into<String>,
187+
operation: impl Into<String>,
188+
) -> Self {
189+
Self::NetworkError {
190+
error_message: source.to_string(),
191+
url: url.into(),
192+
operation: operation.into(),
193+
}
194+
}
195+
196+
/// Create a new encoding error
197+
pub fn encoding_error(format: impl Into<String>, message: impl Into<String>) -> Self {
198+
Self::EncodingError {
199+
format: format.into(),
200+
message: message.into(),
201+
}
202+
}
203+
204+
/// Create a new configuration error
205+
pub fn config_error(field: impl Into<String>, message: impl Into<String>) -> Self {
206+
Self::ConfigError {
207+
field: field.into(),
208+
message: message.into(),
209+
}
210+
}
211+
}
212+
213+
impl fmt::Display for AndromedaError {
214+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215+
match self {
216+
AndromedaError::FsError {
217+
operation, path, ..
218+
} => {
219+
write!(f, "File system error during {}: {}", operation, path)
220+
}
221+
AndromedaError::ParseError { source_path, .. } => {
222+
write!(f, "Parse error in {}", source_path)
223+
}
224+
AndromedaError::RuntimeError { message, .. } => {
225+
write!(f, "Runtime error: {}", message)
226+
}
227+
AndromedaError::ExtensionError {
228+
extension_name,
229+
message,
230+
} => {
231+
write!(f, "Extension '{}' error: {}", extension_name, message)
232+
}
233+
AndromedaError::ResourceError {
234+
rid,
235+
operation,
236+
message,
237+
} => {
238+
write!(
239+
f,
240+
"Resource {} error during {}: {}",
241+
rid, operation, message
242+
)
243+
}
244+
AndromedaError::TaskError { task_id, message } => {
245+
write!(f, "Task {} error: {}", task_id, message)
246+
}
247+
AndromedaError::NetworkError { url, operation, .. } => {
248+
write!(f, "Network error during {} for {}", operation, url)
249+
}
250+
AndromedaError::EncodingError { format, message } => {
251+
write!(f, "Encoding error ({}): {}", format, message)
252+
}
253+
AndromedaError::ConfigError { field, message } => {
254+
write!(f, "Configuration error in field '{}': {}", field, message)
255+
}
256+
}
257+
}
258+
}
259+
260+
impl std::error::Error for AndromedaError {}
261+
262+
/// Result type alias for Andromeda operations
263+
pub type AndromedaResult<T> = Result<T, AndromedaError>;
264+
265+
/// Enhanced error reporting utilities
266+
pub struct ErrorReporter;
267+
268+
impl ErrorReporter {
269+
/// Print a formatted error with miette
270+
pub fn print_error(error: &AndromedaError) {
271+
eprintln!();
272+
eprintln!(
273+
"{} {}",
274+
"🚨".red().bold(),
275+
"Andromeda Runtime Error".bright_red().bold()
276+
);
277+
eprintln!("{}", "─".repeat(50).red());
278+
279+
// Use Display instead of miette for now to avoid trait issues
280+
eprintln!("{}", error);
281+
}
282+
283+
/// Print multiple errors
284+
pub fn print_errors(errors: &[AndromedaError]) {
285+
eprintln!();
286+
eprintln!(
287+
"{} {} ({} error{})",
288+
"🚨".red().bold(),
289+
"Andromeda Runtime Errors".bright_red().bold(),
290+
errors.len(),
291+
if errors.len() == 1 { "" } else { "s" }
292+
);
293+
eprintln!("{}", "─".repeat(50).red());
294+
295+
for (i, error) in errors.iter().enumerate() {
296+
if errors.len() > 1 {
297+
eprintln!();
298+
eprintln!(
299+
"{} Error {} of {}:",
300+
"📍".cyan(),
301+
(i + 1).to_string().bright_cyan(),
302+
errors.len().to_string().bright_cyan()
303+
);
304+
}
305+
eprintln!("{}", error);
306+
}
307+
}
308+
309+
/// Create a formatted error report as string
310+
pub fn format_error(error: &AndromedaError) -> String {
311+
error.to_string()
312+
}
313+
}
314+
315+
/// Trait for converting various error types to AndromedaError
316+
pub trait IntoAndromedaError<T> {
317+
fn into_andromeda_error(self) -> AndromedaResult<T>;
318+
}
319+
320+
impl<T> IntoAndromedaError<T> for Result<T, std::io::Error> {
321+
fn into_andromeda_error(self) -> AndromedaResult<T> {
322+
self.map_err(|e| AndromedaError::fs_error(e, "unknown", "unknown"))
323+
}
324+
}
325+
326+
/// Macro for quick error creation
327+
#[macro_export]
328+
macro_rules! andromeda_error {
329+
(fs: $op:expr, $path:expr, $source:expr) => {
330+
$crate::AndromedaError::fs_error($source, $op, $path)
331+
};
332+
(runtime: $msg:expr) => {
333+
$crate::AndromedaError::runtime_error($msg)
334+
};
335+
(extension: $name:expr, $msg:expr) => {
336+
$crate::AndromedaError::extension_error($name, $msg)
337+
};
338+
(resource: $rid:expr, $op:expr, $msg:expr) => {
339+
$crate::AndromedaError::resource_error($rid, $op, $msg)
340+
};
341+
(task: $id:expr, $msg:expr) => {
342+
$crate::AndromedaError::task_error($id, $msg)
343+
};
344+
(encoding: $format:expr, $msg:expr) => {
345+
$crate::AndromedaError::encoding_error($format, $msg)
346+
};
347+
(config: $field:expr, $msg:expr) => {
348+
$crate::AndromedaError::config_error($field, $msg)
349+
};
350+
}

0 commit comments

Comments
 (0)