1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//! Wait builtin — wait for background jobs to complete.
//!
//! Accesses the shared job table via [`ShellRef`](super::ShellRef).
use async_trait::async_trait;
use super::{Builtin, Context};
use crate::error::Result;
use crate::interpreter::{BuiltinSideEffect, ExecResult};
/// `wait` builtin — wait for background jobs to complete.
///
/// Usage: wait [JOB_ID...]
///
/// If no JOB_ID is specified, wait for all background jobs.
/// Returns the exit status of the last job waited for.
/// Merges background job stdout/stderr into the result.
pub struct Wait;
#[async_trait]
impl Builtin for Wait {
async fn execute(&self, ctx: Context<'_>) -> Result<ExecResult> {
let Some(shell) = ctx.shell.as_ref() else {
// No shell state — no-op (no jobs to wait for)
return Ok(ExecResult::ok(String::new()));
};
let jobs = shell.jobs();
let mut last_exit_code = 0i32;
let mut stdout = String::new();
let mut stderr = String::new();
if ctx.args.is_empty() {
// Wait for all background jobs, collecting their output
let results = jobs.lock().await.wait_all_results().await;
for r in results {
stdout.push_str(&r.stdout);
stderr.push_str(&r.stderr);
last_exit_code = r.exit_code;
}
} else {
// Wait for specific job IDs
for arg in ctx.args {
if let Ok(job_id) = arg.parse::<usize>()
&& let Some(r) = jobs.lock().await.wait_for(job_id).await
{
stdout.push_str(&r.stdout);
stderr.push_str(&r.stderr);
last_exit_code = r.exit_code;
}
}
}
let mut result = ExecResult {
stdout,
stderr,
exit_code: last_exit_code,
..Default::default()
};
result
.side_effects
.push(BuiltinSideEffect::SetLastExitCode(last_exit_code));
Ok(result)
}
}
// Integration tests for wait are in tests/interpreter_tests.rs
// (wait needs the full interpreter to have meaningful background jobs)