Skip to content

Commit fe38825

Browse files
authored
feat(core): better error context (#85)
1 parent 62fd1ee commit fe38825

File tree

8 files changed

+598
-115
lines changed

8 files changed

+598
-115
lines changed

cli/src/error.rs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -322,24 +322,3 @@ pub fn read_file_with_context(path: &std::path::Path) -> Result<String> {
322322
_ => AndromedaError::file_read_error(path.to_path_buf(), e),
323323
})
324324
}
325-
326-
/// Extract meaningful error information from Nova VM errors
327-
pub fn extract_runtime_error_info(
328-
error_string: &str,
329-
_file_path: Option<String>,
330-
) -> (String, Option<u32>, Option<u32>) {
331-
for part in error_string.split_whitespace() {
332-
if let Some(colon_pos) = part.rfind(':') {
333-
if let Some(second_colon) = part[..colon_pos].rfind(':') {
334-
let line_str = &part[second_colon + 1..colon_pos];
335-
let col_str = &part[colon_pos + 1..];
336-
337-
if let (Ok(line), Ok(col)) = (line_str.parse::<u32>(), col_str.parse::<u32>()) {
338-
return (error_string.to_string(), Some(line), Some(col));
339-
}
340-
}
341-
}
342-
}
343-
344-
(error_string.to_string(), None, None)
345-
}

cli/src/main.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ mod run;
1717
mod styles;
1818
use run::run;
1919
mod error;
20-
use error::{AndromedaError, Result, init_error_reporting, print_error};
20+
use error::{Result, init_error_reporting, print_error};
2121
mod format;
2222
use format::format_file;
2323
mod helper;
@@ -108,8 +108,11 @@ enum Command {
108108
}
109109

110110
fn main() {
111-
// Initialize beautiful error reporting
111+
// Initialize beautiful error reporting from CLI
112112
init_error_reporting();
113+
// Also initialize the enhanced error reporting from core
114+
andromeda_core::ErrorReporter::init();
115+
113116
if let Err(error) = run_main() {
114117
print_error(error);
115118
std::process::exit(1);
@@ -137,7 +140,7 @@ fn run_main() -> Result<()> {
137140
.enable_time()
138141
.build()
139142
.map_err(|e| {
140-
AndromedaError::config_error(
143+
error::AndromedaError::config_error(
141144
"Failed to initialize async runtime".to_string(),
142145
None,
143146
Some(Box::new(e)),
@@ -161,7 +164,7 @@ fn run_main() -> Result<()> {
161164
}
162165
Command::Compile { path, out } => {
163166
compile(out.as_path(), path.as_path()).map_err(|e| {
164-
AndromedaError::compile_error(
167+
error::AndromedaError::compile_error(
165168
format!("Compilation failed: {e}"),
166169
path.clone(),
167170
out.clone(),
@@ -176,8 +179,9 @@ fn run_main() -> Result<()> {
176179
expose_internals,
177180
print_internals,
178181
disable_gc,
179-
} => run_repl(expose_internals, print_internals, disable_gc)
180-
.map_err(|e| AndromedaError::repl_error(format!("REPL failed: {e}"), Some(e))),
182+
} => run_repl(expose_internals, print_internals, disable_gc).map_err(|e| {
183+
error::AndromedaError::repl_error(format!("REPL failed: {e}"), Some(e))
184+
}),
181185
Command::Fmt { paths } => {
182186
let files_to_format = find_formattable_files(&paths)?;
183187

@@ -201,7 +205,7 @@ fn run_main() -> Result<()> {
201205
version,
202206
dry_run,
203207
} => upgrade::run_upgrade(force, version, dry_run).map_err(|e| {
204-
AndromedaError::runtime_error(
208+
error::AndromedaError::runtime_error(
205209
format!("Upgrade failed: {e}"),
206210
None,
207211
None,
@@ -213,7 +217,7 @@ fn run_main() -> Result<()> {
213217
});
214218
match rt.block_on(nova_thread) {
215219
Ok(result) => result,
216-
Err(e) => Err(AndromedaError::config_error(
220+
Err(e) => Err(error::AndromedaError::config_error(
217221
"Runtime execution failed".to_string(),
218222
None,
219223
Some(Box::new(e)),

cli/src/run.rs

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5-
use crate::error::{AndromedaError, Result, extract_runtime_error_info, read_file_with_context};
6-
use andromeda_core::{HostData, Runtime, RuntimeConfig, RuntimeFile};
5+
use crate::error::{Result, read_file_with_context};
6+
use andromeda_core::{
7+
AndromedaError, ErrorReporter, HostData, Runtime, RuntimeConfig, RuntimeFile,
8+
};
79
use andromeda_runtime::{
810
recommended_builtins, recommended_eventloop_handler, recommended_extensions,
911
};
@@ -12,7 +14,7 @@ use andromeda_runtime::{
1214
pub fn run(verbose: bool, no_strict: bool, files: Vec<RuntimeFile>) -> Result<()> {
1315
// Validate that we have files to run
1416
if files.is_empty() {
15-
return Err(AndromedaError::invalid_argument(
17+
return Err(crate::error::AndromedaError::invalid_argument(
1618
"files".to_string(),
1719
"at least one file path".to_string(),
1820
"empty list".to_string(),
@@ -24,7 +26,7 @@ pub fn run(verbose: bool, no_strict: bool, files: Vec<RuntimeFile>) -> Result<()
2426
if let RuntimeFile::Local { path } = file {
2527
let file_path = std::path::Path::new(path);
2628
if !file_path.exists() {
27-
return Err(AndromedaError::file_not_found(
29+
return Err(crate::error::AndromedaError::file_not_found(
2830
file_path.to_path_buf(),
2931
std::io::Error::new(std::io::ErrorKind::NotFound, "File not found"),
3032
));
@@ -38,6 +40,15 @@ pub fn run(verbose: bool, no_strict: bool, files: Vec<RuntimeFile>) -> Result<()
3840
let (macro_task_tx, macro_task_rx) = std::sync::mpsc::channel();
3941
let host_data = HostData::new(macro_task_tx);
4042

43+
// Store file information before moving files into runtime
44+
let first_file_info = files.first().and_then(|file| {
45+
if let RuntimeFile::Local { path } = file {
46+
Some(path.clone())
47+
} else {
48+
None
49+
}
50+
});
51+
4152
let runtime = Runtime::new(
4253
RuntimeConfig {
4354
no_strict,
@@ -74,33 +85,45 @@ pub fn run(verbose: bool, no_strict: bool, files: Vec<RuntimeFile>) -> Result<()
7485
.to_string()
7586
});
7687

77-
// Try to extract file path from the error context if available
78-
let file_path =
79-
runtime_output
80-
.agent
81-
.run_in_realm(&runtime_output.realm_root, |_agent, _gc| {
82-
// This is a simplified approach - Nova might provide better error context in the future
83-
None::<String>
84-
});
88+
// Try to get the first file from our runtime files to show source context
89+
let (file_path, source_content) = if let Some(path) = first_file_info {
90+
match read_file_with_context(std::path::Path::new(&path)) {
91+
Ok(content) => (Some(path), Some(content)),
92+
Err(_) => (Some(path), None),
93+
}
94+
} else {
95+
(None, None)
96+
};
8597

86-
// Extract line and column information from the error message
87-
let (clean_message, line, column) =
88-
extract_runtime_error_info(&error_message, file_path.clone());
98+
// Create an enhanced runtime error with source context if available
99+
let enhanced_error = if let (Some(path), Some(content)) = (file_path, source_content) {
100+
// Try to find a better source span by looking for the error location in the message
101+
let source_span = if error_message.contains("fertch") {
102+
// Find the position of 'fertch' in the source code
103+
if let Some(pos) = content.find("fertch") {
104+
miette::SourceSpan::new(pos.into(), 6) // 'fertch' is 6 characters
105+
} else {
106+
miette::SourceSpan::new(0.into(), 1)
107+
}
108+
} else {
109+
miette::SourceSpan::new(0.into(), 1)
110+
};
89111

90-
// Try to get source content if we have a file path
91-
let source_content = if let Some(ref path) = file_path {
92-
read_file_with_context(std::path::Path::new(path)).ok()
112+
AndromedaError::runtime_error_with_location(
113+
error_message.clone(),
114+
&content,
115+
&path,
116+
source_span,
117+
)
93118
} else {
94-
None
119+
AndromedaError::runtime_error(error_message.clone())
95120
};
96121

97-
Err(AndromedaError::runtime_error(
98-
clean_message,
99-
file_path,
100-
line,
101-
column,
102-
source_content,
103-
))
122+
// Print the enhanced error using our error reporting system
123+
ErrorReporter::print_error(&enhanced_error);
124+
125+
// Exit directly instead of returning another error to avoid double printing
126+
std::process::exit(1);
104127
}
105128
}
106129
}

0 commit comments

Comments
 (0)