1
0
Fork 0
mirror of https://github.com/imjasonh/testscript-rs synced 2026-07-12 10:58:27 +00:00

improve error messages with context (#13)

Signed-off-by: Jason Hall <jason@chainguard.dev>
This commit is contained in:
Jason Hall 2025-09-26 19:55:13 -04:00 committed by GitHub
parent 38bef8a370
commit 3a8e953f70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 156 additions and 3 deletions

View file

@ -46,6 +46,16 @@ pub enum Error {
/// Generic error with message
#[error("{0}")]
Generic(String),
/// Script execution error with context
#[error("Error in {script_file} at line {line_num}:\n{context}")]
ScriptError {
script_file: String,
line_num: usize,
context: String,
#[source]
source: Box<Error>,
},
}
impl Error {
@ -64,4 +74,45 @@ impl Error {
message: message.into(),
}
}
/// Create a script error with context
pub fn script_error(
script_file: impl Into<String>,
line_num: usize,
script_content: &str,
source: Error,
) -> Self {
let context = generate_error_context(script_content, line_num);
Error::ScriptError {
script_file: script_file.into(),
line_num,
context,
source: Box::new(source),
}
}
}
/// Generate error context showing surrounding lines
fn generate_error_context(script_content: &str, error_line: usize) -> String {
let lines: Vec<&str> = script_content.lines().collect();
let mut context = String::new();
let start = error_line.saturating_sub(3).max(1);
let end = (error_line + 2).min(lines.len());
for line_num in start..=end {
if line_num == 0 {
continue;
}
let line_content = lines.get(line_num - 1).unwrap_or(&"");
if line_num == error_line {
context.push_str(&format!("> {} | {}\n", line_num, line_content));
} else {
context.push_str(&format!(" {} | {}\n", line_num, line_content));
}
}
context.trim_end().to_string()
}

View file

@ -659,7 +659,21 @@ pub fn run_test(script_path: &Path) -> Result<()> {
pub fn run_script(script_path: &Path, params: &RunParams) -> Result<()> {
// Read and parse the script
let content = fs::read_to_string(script_path)?;
let script = crate::parser::parse(&content)?;
let script = crate::parser::parse(&content).map_err(|e| {
// Enhance parse errors with script context
match e {
Error::Parse { line, message } => Error::script_error(
script_path.to_string_lossy().to_string(),
line,
&content,
Error::Parse { line, message },
),
other => other,
}
})?;
// Create script context for better error reporting
let script_file = script_path.to_string_lossy().to_string();
// Create test environment
let mut env = TestEnvironment::new()?;
@ -678,7 +692,15 @@ pub fn run_script(script_path: &Path, params: &RunParams) -> Result<()> {
// Execute commands
for command in &script.commands {
execute_command(&mut env, command, params)?;
if let Err(e) = execute_command(&mut env, command, params) {
// Wrap error with script context
return Err(Error::script_error(
&script_file,
command.line_num,
&content,
e,
));
}
// Check for early termination
if env.should_skip {