diff --git a/crates/vite_task/src/session/event.rs b/crates/vite_task/src/session/event.rs index ded050ff..6f997b8f 100644 --- a/crates/vite_task/src/session/event.rs +++ b/crates/vite_task/src/session/event.rs @@ -2,6 +2,47 @@ use std::{process::ExitStatus, time::Duration}; use super::cache::CacheMiss; +/// The cache operation that failed. +#[derive(Debug)] +pub enum CacheErrorKind { + /// Cache lookup (`try_hit`) failed. + Lookup, + /// Writing the cache entry failed after successful execution. + Update, +} + +impl std::fmt::Display for CacheErrorKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Lookup => f.write_str("lookup"), + Self::Update => f.write_str("update"), + } + } +} + +/// Error that occurred during a leaf execution. +/// +/// Reported through [`super::reporter::LeafExecutionReporter::finish()`] and +/// displayed by the reporter. +#[derive(Debug, thiserror::Error)] +pub enum ExecutionError { + /// A cache operation failed. + #[error("Cache {kind} failed")] + Cache { + kind: CacheErrorKind, + #[source] + source: anyhow::Error, + }, + + /// The OS failed to spawn the child process (e.g., command not found). + #[error("Failed to spawn process")] + Spawn(#[source] anyhow::Error), + + /// Creating the post-run fingerprint failed after successful execution. + #[error("Failed to create post-run fingerprint")] + PostRunFingerprint(#[source] anyhow::Error), +} + #[derive(Debug)] pub enum OutputKind { Stdout, diff --git a/crates/vite_task/src/session/execute/mod.rs b/crates/vite_task/src/session/execute/mod.rs index 469e7f92..90c71dca 100644 --- a/crates/vite_task/src/session/execute/mod.rs +++ b/crates/vite_task/src/session/execute/mod.rs @@ -14,7 +14,8 @@ use self::{ use super::{ cache::{CommandCacheValue, ExecutionCache}, event::{ - CacheDisabledReason, CacheNotUpdatedReason, CacheStatus, CacheUpdateStatus, OutputKind, + CacheDisabledReason, CacheErrorKind, CacheNotUpdatedReason, CacheStatus, CacheUpdateStatus, + ExecutionError, OutputKind, }, reporter::{ ExitStatus, GraphExecutionReporter, GraphExecutionReporterBuilder, LeafExecutionPath, @@ -186,7 +187,7 @@ pub async fn execute_spawn( leaf_reporter.finish( None, CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled), - Some(vite_str::format!("Cache lookup failed: {err}")), + Some(ExecutionError::Cache { kind: CacheErrorKind::Lookup, source: err }), ); return SpawnOutcome::Failed; } @@ -263,7 +264,7 @@ pub async fn execute_spawn( leaf_reporter.finish( None, CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled), - Some(vite_str::format!("Failed to spawn process: {err}")), + Some(ExecutionError::Spawn(err)), ); return SpawnOutcome::Failed; } @@ -293,13 +294,16 @@ pub async fn execute_spawn( Ok(()) => (CacheUpdateStatus::Updated, None), Err(err) => ( CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled), - Some(vite_str::format!("Failed to update cache: {err}")), + Some(ExecutionError::Cache { + kind: CacheErrorKind::Update, + source: err, + }), ), } } Err(err) => ( CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled), - Some(vite_str::format!("Failed to create post-run fingerprint: {err}")), + Some(ExecutionError::PostRunFingerprint(err)), ), } } else { diff --git a/crates/vite_task/src/session/reporter/labeled.rs b/crates/vite_task/src/session/reporter/labeled.rs index c6d72ea2..781ae6ef 100644 --- a/crates/vite_task/src/session/reporter/labeled.rs +++ b/crates/vite_task/src/session/reporter/labeled.rs @@ -25,7 +25,7 @@ use super::{ }; use crate::session::{ cache::format_cache_status_summary, - event::{CacheStatus, CacheUpdateStatus, OutputKind, exit_status_to_code}, + event::{CacheStatus, CacheUpdateStatus, ExecutionError, OutputKind, exit_status_to_code}, }; /// Information tracked for each leaf execution, used in the final summary. @@ -273,13 +273,16 @@ impl LeafExecutionReporter for LabeledLeafReporter { self: Box, status: Option, _cache_update_status: CacheUpdateStatus, - error: Option, + error: Option, ) { let mut shared = self.shared.borrow_mut(); - // Handle errors - if let Some(ref message) = error { - write_error_message(&mut shared.writer, message); + // Handle errors — format the full error chain using anyhow's `{:#}` formatter + // (joins cause chain with `: ` separators). + let has_error = error.is_some(); + if let Some(error) = error { + let message: Str = vite_str::format!("{:#}", anyhow::Error::from(error)); + write_error_message(&mut shared.writer, &message); // Update the execution info if start() was called (an entry was pushed). // Without the `self.started` guard, `last_mut()` would return a @@ -287,7 +290,7 @@ impl LeafExecutionReporter for LabeledLeafReporter { if self.started && let Some(exec) = shared.executions.last_mut() { - exec.error_message = Some(message.clone()); + exec.error_message = Some(message); } shared.stats.failed += 1; @@ -295,7 +298,7 @@ impl LeafExecutionReporter for LabeledLeafReporter { // Update failure statistics for non-zero exit status (not an error, just a failed task) // None means success (cache hit or in-process), Some checks the actual exit status - if error.is_none() && status.is_some_and(|s| !s.success()) { + if !has_error && status.is_some_and(|s| !s.success()) { shared.stats.failed += 1; } diff --git a/crates/vite_task/src/session/reporter/mod.rs b/crates/vite_task/src/session/reporter/mod.rs index f4cde301..30d78876 100644 --- a/crates/vite_task/src/session/reporter/mod.rs +++ b/crates/vite_task/src/session/reporter/mod.rs @@ -42,7 +42,7 @@ use vite_task_plan::{ExecutionGraph, ExecutionItem, ExecutionItemDisplay, Execut use super::{ cache::format_cache_status_inline, - event::{CacheStatus, CacheUpdateStatus, OutputKind}, + event::{CacheStatus, CacheUpdateStatus, ExecutionError, OutputKind}, }; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -241,7 +241,7 @@ pub trait LeafExecutionReporter { self: Box, status: Option, cache_update_status: CacheUpdateStatus, - error: Option, + error: Option, ); } diff --git a/crates/vite_task/src/session/reporter/plain.rs b/crates/vite_task/src/session/reporter/plain.rs index 88a81942..648487ee 100644 --- a/crates/vite_task/src/session/reporter/plain.rs +++ b/crates/vite_task/src/session/reporter/plain.rs @@ -6,10 +6,9 @@ use std::io::Write; use bstr::BString; -use vite_str::Str; use super::{LeafExecutionReporter, StdinSuggestion, write_cache_hit_message, write_error_message}; -use crate::session::event::{CacheStatus, CacheUpdateStatus, OutputKind}; +use crate::session::event::{CacheStatus, CacheUpdateStatus, ExecutionError, OutputKind}; /// A self-contained [`LeafExecutionReporter`] for single-leaf executions /// (e.g., `execute_synthetic`). @@ -71,11 +70,12 @@ impl LeafExecutionReporter for PlainReporter { mut self: Box, _status: Option, _cache_update_status: CacheUpdateStatus, - error: Option, + error: Option, ) { - // Handle errors — print inline error message - if let Some(ref message) = error { - write_error_message(&mut self.writer, message); + // Handle errors — format the full error chain and print inline. + if let Some(error) = error { + let message = vite_str::format!("{:#}", anyhow::Error::from(error)); + write_error_message(&mut self.writer, &message); return; }