1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4
5use crate::{RuntimeKind, SpawnRequest};
6
7#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
8pub struct LaunchEnv {
9 pub key: String,
10 pub value: String,
11}
12
13impl LaunchEnv {
14 pub fn new(key: impl Into<String>, value: impl Into<String>) -> Self {
15 Self {
16 key: key.into(),
17 value: value.into(),
18 }
19 }
20}
21
22#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
23pub struct LaunchSpec {
24 pub argv: Vec<String>,
25 pub env: Vec<LaunchEnv>,
26 pub cwd: PathBuf,
27 #[serde(default, skip_serializing_if = "Option::is_none")]
28 pub shell_resume: Option<ShellResume>,
29}
30
31impl LaunchSpec {
32 pub fn command(&self) -> Result<&str, LauncherError> {
33 self.argv
34 .first()
35 .map(String::as_str)
36 .ok_or(LauncherError::EmptyArgv)
37 }
38}
39
40#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
41pub struct ShellResume {
42 pub argv: Vec<String>,
43 pub env: Vec<LaunchEnv>,
44 pub cwd: PathBuf,
45}
46
47impl ShellResume {
48 pub fn command(&self) -> Result<&str, LauncherError> {
49 self.argv
50 .first()
51 .map(String::as_str)
52 .ok_or(LauncherError::EmptyShellArgv)
53 }
54}
55
56pub trait RuntimeLauncher: Sync {
57 fn kind(&self) -> RuntimeKind;
58
59 fn argv(&self, request: &SpawnRequest) -> Result<Vec<String>, LauncherError>;
60
61 fn env(&self, request: &SpawnRequest) -> Result<Vec<LaunchEnv>, LauncherError>;
62
63 fn cwd(&self, request: &SpawnRequest) -> Result<PathBuf, LauncherError> {
64 Ok(request.cwd.clone())
65 }
66
67 fn launch_spec(&self, request: &SpawnRequest) -> Result<LaunchSpec, LauncherError> {
68 let spec = LaunchSpec {
69 argv: self.argv(request)?,
70 env: self.env(request)?,
71 cwd: self.cwd(request)?,
72 shell_resume: request.shell_resume.clone(),
73 };
74 if spec.argv.is_empty() {
75 return Err(LauncherError::EmptyArgv);
76 }
77 if spec.env.is_empty() {
78 return Err(LauncherError::EmptyEnv {
79 runtime_kind: self.kind().to_string(),
80 });
81 }
82 Ok(spec)
83 }
84}
85
86#[derive(Clone, Debug, thiserror::Error)]
87pub enum LauncherError {
88 #[error("no launcher registered for runtime kind: {runtime_kind}")]
89 NoLauncher { runtime_kind: String },
90 #[error("launcher produced empty argv")]
91 EmptyArgv,
92 #[error("launcher produced empty shell resume argv")]
93 EmptyShellArgv,
94 #[error("launcher {runtime_kind} produced empty env")]
95 EmptyEnv { runtime_kind: String },
96 #[error("failed to resolve launcher binary {binary}: {message}")]
97 BinaryLookupFailed { binary: String, message: String },
98}