Skip to main content

lilo_rm_core/
launcher.rs

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}