artifactize_tools/launch.rs
1//! How the `help` built-in starts its program. A host with a process layer of its own passes
2//! it as a [`Launcher`]; the standalone command uses [`Standalone`], which keeps the program
3//! and its children in one process group or Job Object and gives it an explicit environment.
4
5use std::{
6 collections::BTreeMap, ffi::OsString, future::Future, path::PathBuf, pin::Pin, time::Duration,
7};
8
9use tokio_util::sync::CancellationToken;
10
11use crate::platform;
12
13/// One resolved program to run: its path, its arguments and its working directory.
14#[derive(Debug, Clone)]
15pub struct Launch {
16 pub program: PathBuf,
17 pub args: Vec<OsString>,
18 pub cwd: PathBuf,
19 /// How long the program may run before it and its children are killed.
20 pub timeout: Duration,
21 /// How many bytes of each output stream are kept; more sets `truncated`.
22 pub output_limit: usize,
23}
24
25/// What a finished program produced.
26#[derive(Debug)]
27pub struct Finished {
28 /// The actual exit status, not independently editable success and diagnostic fields.
29 pub status: std::process::ExitStatus,
30 pub stdout: Vec<u8>,
31 pub stderr: Vec<u8>,
32 /// An output stream exceeded `output_limit`.
33 pub truncated: bool,
34}
35
36/// Why a program did not finish.
37#[derive(Debug, thiserror::Error)]
38pub enum LaunchError {
39 #[error("timed out")]
40 TimedOut,
41 #[error("Tool call was cancelled.")]
42 Cancelled,
43 #[error("{0}")]
44 Failed(String),
45}
46
47/// The boxed future a [`Launcher`] returns, so that hosts can pass one as a trait object.
48pub type Running<'a> = Pin<Box<dyn Future<Output = Result<Finished, LaunchError>> + Send + 'a>>;
49
50/// Starts a built-in's program and waits for it.
51pub trait Launcher: Sync {
52 fn run<'a>(&'a self, launch: Launch, cancellation: &'a CancellationToken) -> Running<'a>;
53}
54
55/// The standalone command's launcher: the program runs in its own process group or Job
56/// Object, which is killed when it finishes, times out or is cancelled, with only the
57/// variables in [`passed_environment`].
58pub struct Standalone;
59
60impl Launcher for Standalone {
61 fn run<'a>(&'a self, launch: Launch, cancellation: &'a CancellationToken) -> Running<'a> {
62 Box::pin(platform::process::run(launch, cancellation))
63 }
64}
65
66/// The variables a standalone program receives: the whole environment of this process, as in
67/// the author's terminal, without artifactize's own `ARTIFACTIZE_*` variables.
68pub fn passed_environment() -> BTreeMap<OsString, OsString> {
69 platform::process::passed_environment()
70}