1use std::collections::HashMap;
2
3use anyhow::{Result, bail};
4use oxdock_fs::{CargoScratch, GuardedPath, PolicyPath};
5#[allow(clippy::disallowed_types, clippy::disallowed_methods)]
6use std::process::ExitStatus;
7
8use std::sync::Arc;
9
10#[cfg(not(miri))]
15pub use oxdock_pipe::{OsPipeReader, OsPipeWriter, create_os_pipe};
16pub use oxdock_pipe::{SharedInput, SharedOutput};
17
18#[derive(Clone, Debug)]
22pub struct CommandContext {
23 cwd: PolicyPath,
24 envs: Arc<HashMap<String, String>>,
25 cargo_target_dir: CargoScratch,
26 workspace_root: GuardedPath,
27 build_context: GuardedPath,
28}
29
30impl CommandContext {
31 pub fn new(
32 cwd: &PolicyPath,
33 envs: Arc<HashMap<String, String>>,
34 cargo_target_dir: &CargoScratch,
35 workspace_root: &GuardedPath,
36 build_context: &GuardedPath,
37 ) -> Self {
38 Self {
39 cwd: cwd.clone(),
40 envs,
41 cargo_target_dir: cargo_target_dir.clone(),
42 workspace_root: workspace_root.clone(),
43 build_context: build_context.clone(),
44 }
45 }
46
47 pub fn from_map(
49 cwd: &PolicyPath,
50 envs: &HashMap<String, String>,
51 cargo_target_dir: &CargoScratch,
52 workspace_root: &GuardedPath,
53 build_context: &GuardedPath,
54 ) -> Self {
55 Self::new(
56 cwd,
57 Arc::new(envs.clone()),
58 cargo_target_dir,
59 workspace_root,
60 build_context,
61 )
62 }
63
64 pub fn cwd(&self) -> &PolicyPath {
65 &self.cwd
66 }
67
68 pub fn envs(&self) -> &Arc<HashMap<String, String>> {
69 &self.envs
70 }
71
72 pub fn cargo_target_dir(&self) -> &CargoScratch {
73 &self.cargo_target_dir
74 }
75
76 pub fn workspace_root(&self) -> &GuardedPath {
77 &self.workspace_root
78 }
79
80 pub fn build_context(&self) -> &GuardedPath {
81 &self.build_context
82 }
83}
84
85pub trait BackgroundHandle: Send {
87 fn try_wait(&mut self) -> Result<Option<ExitStatus>>;
88 fn kill(&mut self) -> Result<()>;
89 fn wait(&mut self) -> Result<ExitStatus>;
90}
91
92#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
93pub enum CommandMode {
94 #[default]
95 Foreground,
96 Background,
97}
98
99#[derive(Clone, Default)]
100pub enum CommandStdout {
101 #[default]
102 Inherit,
103 Stream(SharedOutput),
104 Capture,
105 #[cfg(not(miri))]
110 OsPipe(OsPipeWriter),
111}
112
113#[derive(Clone, Default)]
114pub enum CommandStdin {
115 #[default]
117 Null,
118 Inherit,
119 Stream(SharedInput),
120 #[cfg(not(miri))]
123 OsPipe(OsPipeReader),
124}
125
126impl From<Option<SharedInput>> for CommandStdin {
127 fn from(stdin: Option<SharedInput>) -> Self {
128 match stdin {
129 Some(reader) => CommandStdin::Stream(reader),
130 None => CommandStdin::Null,
131 }
132 }
133}
134
135#[derive(Clone, Default)]
136pub enum CommandStderr {
137 #[default]
138 Inherit,
139 Stream(SharedOutput),
140 #[cfg(not(miri))]
144 OsPipe(OsPipeWriter),
145}
146
147#[derive(Clone, Default)]
148pub struct CommandOptions {
149 pub mode: CommandMode,
150 pub stdin: CommandStdin,
151 pub stdout: CommandStdout,
152 pub stderr: CommandStderr,
153}
154
155impl CommandOptions {
156 pub fn foreground() -> Self {
157 Self::default()
158 }
159
160 pub fn background() -> Self {
161 Self {
162 mode: CommandMode::Background,
163 ..Self::default()
164 }
165 }
166}
167
168pub enum CommandResult<H> {
169 Completed,
170 Captured(Vec<u8>),
171 Background(H),
172}
173
174pub const INHERIT_STDOUT_ENV_VAR: &str = "OXDOCK_INHERIT_STDOUT";
180
181pub const PROCESS_DEBUG_ENV_VAR: &str = "OXBOOK_DEBUG";
185
186pub trait ProcessManager: Clone + Send + 'static {
191 type Handle: BackgroundHandle + Clone + Send + 'static;
192
193 fn run_command(
194 &mut self,
195 ctx: &CommandContext,
196 script: &str,
197 options: CommandOptions,
198 ) -> Result<CommandResult<Self::Handle>>;
199
200 fn spawn_command(
204 &mut self,
205 ctx: &CommandContext,
206 script: &str,
207 options: CommandOptions,
208 ) -> Result<CommandResult<Self::Handle>> {
209 self.run_command(ctx, script, options)
210 }
211
212 fn run_argv(
217 &mut self,
218 _ctx: &CommandContext,
219 argv: &[String],
220 _options: CommandOptions,
221 ) -> Result<CommandResult<Self::Handle>> {
222 bail!("run_argv not implemented for argv {argv:?}")
223 }
224
225 fn spawn_argv(
228 &mut self,
229 ctx: &CommandContext,
230 argv: &[String],
231 options: CommandOptions,
232 ) -> Result<CommandResult<Self::Handle>> {
233 self.run_argv(ctx, argv, options)
234 }
235}