Skip to content

Commit c5ee519

Browse files
authored
refactor: inject async writer into reporters instead of hardcoding stdout (#161)
## Summary - Inject `Box<dyn AsyncWrite + Unpin>` writer into `PlainReporter` and `LabeledReporterBuilder` instead of hardcoding `std::io::stdout()` - Make `LeafExecutionReporter::start`/`finish` and `GraphExecutionReporter::finish` async via `#[async_trait(?Send)]` - Convert display helpers to sync formatters returning `Str`/`Vec<u8>`, keeping async I/O only at the reporter method level - Pass `tokio::io::sink()` as writer in unit tests; move reporter tests to their own module files (`plain.rs`, `labeled.rs`)
1 parent 4570ddf commit c5ee519

File tree

5 files changed

+501
-400
lines changed

5 files changed

+501
-400
lines changed

crates/vite_task/src/session/execute/mod.rs

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -120,18 +120,21 @@ impl ExecutionContext<'_> {
120120
LeafExecutionKind::InProcess(in_process_execution) => {
121121
// In-process (built-in) commands: caching is disabled, execute synchronously
122122
let mut stdio_config = leaf_reporter
123-
.start(CacheStatus::Disabled(CacheDisabledReason::InProcessExecution));
123+
.start(CacheStatus::Disabled(CacheDisabledReason::InProcessExecution))
124+
.await;
124125

125126
let execution_output = in_process_execution.execute();
126127
// Write output to the stdout writer from StdioConfig
127128
let _ = stdio_config.stdout_writer.write_all(&execution_output.stdout).await;
128129
let _ = stdio_config.stdout_writer.flush().await;
129130

130-
leaf_reporter.finish(
131-
None,
132-
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
133-
None,
134-
);
131+
leaf_reporter
132+
.finish(
133+
None,
134+
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
135+
None,
136+
)
137+
.await;
135138
}
136139
LeafExecutionKind::Spawn(spawn_execution) => {
137140
#[expect(
@@ -191,11 +194,13 @@ pub async fn execute_spawn(
191194
Err(err) => {
192195
// Cache lookup error — report through finish.
193196
// Note: start() is NOT called because we don't have a valid cache status.
194-
leaf_reporter.finish(
195-
None,
196-
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
197-
Some(ExecutionError::Cache { kind: CacheErrorKind::Lookup, source: err }),
198-
);
197+
leaf_reporter
198+
.finish(
199+
None,
200+
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
201+
Some(ExecutionError::Cache { kind: CacheErrorKind::Lookup, source: err }),
202+
)
203+
.await;
199204
return SpawnOutcome::Failed;
200205
}
201206
}
@@ -206,7 +211,7 @@ pub async fn execute_spawn(
206211

207212
// 2. Report execution start with the determined cache status.
208213
// Returns StdioConfig with the reporter's suggestion and async writers.
209-
let mut stdio_config = leaf_reporter.start(cache_status);
214+
let mut stdio_config = leaf_reporter.start(cache_status).await;
210215

211216
// 3. If cache hit, replay outputs via the StdioConfig writers and finish early.
212217
// No need to actually execute the command — just replay what was cached.
@@ -219,11 +224,9 @@ pub async fn execute_spawn(
219224
let _ = writer.write_all(&output.content).await;
220225
let _ = writer.flush().await;
221226
}
222-
leaf_reporter.finish(
223-
None,
224-
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheHit),
225-
None,
226-
);
227+
leaf_reporter
228+
.finish(None, CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheHit), None)
229+
.await;
227230
return SpawnOutcome::CacheHit;
228231
}
229232

@@ -243,19 +246,23 @@ pub async fn execute_spawn(
243246

244247
match spawn_inherited(&spawn_execution.spawn_command).await {
245248
Ok(result) => {
246-
leaf_reporter.finish(
247-
Some(result.exit_status),
248-
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
249-
None,
250-
);
249+
leaf_reporter
250+
.finish(
251+
Some(result.exit_status),
252+
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
253+
None,
254+
)
255+
.await;
251256
return SpawnOutcome::Spawned(result.exit_status);
252257
}
253258
Err(err) => {
254-
leaf_reporter.finish(
255-
None,
256-
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
257-
Some(ExecutionError::Spawn(err)),
258-
);
259+
leaf_reporter
260+
.finish(
261+
None,
262+
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
263+
Some(ExecutionError::Spawn(err)),
264+
)
265+
.await;
259266
return SpawnOutcome::Failed;
260267
}
261268
}
@@ -280,11 +287,13 @@ pub async fn execute_spawn(
280287
{
281288
Ok(result) => result,
282289
Err(err) => {
283-
leaf_reporter.finish(
284-
None,
285-
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
286-
Some(ExecutionError::Spawn(err)),
287-
);
290+
leaf_reporter
291+
.finish(
292+
None,
293+
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
294+
Some(ExecutionError::Spawn(err)),
295+
)
296+
.await;
288297
return SpawnOutcome::Failed;
289298
}
290299
};
@@ -337,7 +346,7 @@ pub async fn execute_spawn(
337346
// 7. Finish the leaf execution with the result and optional cache error.
338347
// Cache update/fingerprint failures are reported but do not affect the outcome —
339348
// the process ran, so we return its actual exit status.
340-
leaf_reporter.finish(Some(result.exit_status), cache_update_status, cache_error);
349+
leaf_reporter.finish(Some(result.exit_status), cache_update_status, cache_error).await;
341350

342351
SpawnOutcome::Spawned(result.exit_status)
343352
}
@@ -409,6 +418,6 @@ impl Session<'_> {
409418

410419
// Leaf-level errors and non-zero exit statuses are tracked internally
411420
// by the reporter.
412-
reporter.finish()
421+
reporter.finish().await
413422
}
414423
}

crates/vite_task/src/session/mod.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,10 @@ impl<'a> Session<'a> {
246246
.await
247247
}
248248
Ok(graph) => {
249-
let builder = LabeledReporterBuilder::new(self.workspace_path());
249+
let builder = LabeledReporterBuilder::new(
250+
self.workspace_path(),
251+
Box::new(tokio::io::stdout()),
252+
);
250253
Ok(self
251254
.execute_graph(graph, Box::new(builder))
252255
.await
@@ -391,7 +394,8 @@ impl<'a> Session<'a> {
391394

392395
let cwd = Arc::clone(&self.cwd);
393396
let graph = self.plan_from_cli_run(cwd, run_command).await?;
394-
let builder = LabeledReporterBuilder::new(self.workspace_path());
397+
let builder =
398+
LabeledReporterBuilder::new(self.workspace_path(), Box::new(tokio::io::stdout()));
395399
Ok(self.execute_graph(graph, Box::new(builder)).await.err().unwrap_or(ExitStatus::SUCCESS))
396400
}
397401

@@ -467,7 +471,8 @@ impl<'a> Session<'a> {
467471
let cache = self.cache()?;
468472

469473
// Create a plain (standalone) reporter — no graph awareness, no summary
470-
let plain_reporter = reporter::PlainReporter::new(silent_if_cache_hit);
474+
let plain_reporter =
475+
reporter::PlainReporter::new(silent_if_cache_hit, Box::new(tokio::io::stdout()));
471476

472477
// Execute the spawn directly using the free function, bypassing the graph pipeline
473478
match execute::execute_spawn(

0 commit comments

Comments
 (0)