openrunner_rs/
types.rs

1//! Type definitions for OpenRunner script execution.
2
3use std::collections::HashMap;
4use std::path::PathBuf;
5use std::time::Duration;
6use tempfile::NamedTempFile;
7use tokio::process::Child;
8
9/// Configuration options for script execution.
10///
11/// This struct provides fine-grained control over how OpenScript code is executed,
12/// including environment variables, I/O redirection, timeouts, and more.
13///
14/// # Examples
15///
16/// ```rust
17/// use openrunner_rs::ScriptOptions;
18/// use std::time::Duration;
19///
20/// let options = ScriptOptions::new()
21///     .timeout(Duration::from_secs(30))
22///     .env("DEBUG", "1")
23///     .args(vec!["arg1".to_string(), "arg2".to_string()]);
24/// ```
25#[derive(Debug, Clone)]
26pub struct ScriptOptions {
27    pub(crate) openscript_path: Option<PathBuf>,
28    pub(crate) working_directory: Option<PathBuf>,
29    pub(crate) env_vars: HashMap<String, String>,
30    pub(crate) args: Vec<String>,
31    pub(crate) stdin: IoOptions,
32    pub(crate) stdout: IoOptions,
33    pub(crate) stderr: IoOptions,
34    pub(crate) timeout: Option<Duration>,
35    pub(crate) exit_on_error: bool,
36    pub(crate) print_commands: bool,
37    pub(crate) ai_enabled: bool,
38    pub(crate) clear_env: bool,
39}
40
41impl ScriptOptions {
42    /// Create a new `ScriptOptions` with default values.
43    pub fn new() -> Self {
44        Self::default()
45    }
46
47    /// Set the path to the OpenScript interpreter.
48    ///
49    /// If not set, the system will look for `openscript` in the PATH.
50    ///
51    /// # Examples
52    ///
53    /// ```rust
54    /// use openrunner_rs::ScriptOptions;
55    ///
56    /// let options = ScriptOptions::new()
57    ///     .openscript_path("/usr/local/bin/openscript");
58    /// ```
59    pub fn openscript_path(mut self, path: impl Into<PathBuf>) -> Self {
60        self.openscript_path = Some(path.into());
61        self
62    }
63
64    /// Set the working directory for script execution.
65    ///
66    /// # Examples
67    ///
68    /// ```rust
69    /// use openrunner_rs::ScriptOptions;
70    ///
71    /// let options = ScriptOptions::new()
72    ///     .working_directory("/tmp");
73    /// ```
74    pub fn working_directory(mut self, path: impl Into<PathBuf>) -> Self {
75        self.working_directory = Some(path.into());
76        self
77    }
78
79    /// Add an environment variable for script execution.
80    ///
81    /// # Examples
82    ///
83    /// ```rust
84    /// use openrunner_rs::ScriptOptions;
85    ///
86    /// let options = ScriptOptions::new()
87    ///     .env("DEBUG", "1")
88    ///     .env("LOG_LEVEL", "info");
89    /// ```
90    pub fn env(mut self, key: impl Into<String>, val: impl Into<String>) -> Self {
91        self.env_vars.insert(key.into(), val.into());
92        self
93    }
94
95    /// Set command-line arguments for the script.
96    ///
97    /// # Examples
98    ///
99    /// ```rust
100    /// use openrunner_rs::ScriptOptions;
101    ///
102    /// let options = ScriptOptions::new()
103    ///     .args(vec!["--verbose".to_string(), "input.txt".to_string()]);
104    /// ```
105    pub fn args(mut self, args: Vec<String>) -> Self {
106        self.args = args;
107        self
108    }
109
110    /// Configure stdin handling.
111    ///
112    /// # Examples
113    ///
114    /// ```rust
115    /// use openrunner_rs::{types::IoOptions, ScriptOptions};
116    ///
117    /// let options = ScriptOptions::new()
118    ///     .stdin(IoOptions::Null);
119    /// ```
120    pub fn stdin(mut self, opt: IoOptions) -> Self {
121        self.stdin = opt;
122        self
123    }
124
125    /// Configure stdout handling.
126    pub fn stdout(mut self, opt: IoOptions) -> Self {
127        self.stdout = opt;
128        self
129    }
130
131    /// Configure stderr handling.
132    pub fn stderr(mut self, opt: IoOptions) -> Self {
133        self.stderr = opt;
134        self
135    }
136
137    /// Set a timeout for script execution.
138    ///
139    /// If the script runs longer than the specified duration, it will be terminated.
140    ///
141    /// # Examples
142    ///
143    /// ```rust
144    /// use openrunner_rs::ScriptOptions;
145    /// use std::time::Duration;
146    ///
147    /// let options = ScriptOptions::new()
148    ///     .timeout(Duration::from_secs(60));
149    /// ```
150    pub fn timeout(mut self, duration: Duration) -> Self {
151        self.timeout = Some(duration);
152        self
153    }
154
155    /// Configure whether to exit on script errors.
156    pub fn exit_on_error(mut self, exit: bool) -> Self {
157        self.exit_on_error = exit;
158        self
159    }
160
161    /// Configure whether to print commands before execution.
162    pub fn print_commands(mut self, print: bool) -> Self {
163        self.print_commands = print;
164        self
165    }
166
167    /// Configure whether AI features are enabled.
168    pub fn ai_enabled(mut self, enabled: bool) -> Self {
169        self.ai_enabled = enabled;
170        self
171    }
172
173    /// Configure whether to clear the environment before execution.
174    ///
175    /// If true, only explicitly set environment variables will be available.
176    pub fn clear_env(mut self, clear: bool) -> Self {
177        self.clear_env = clear;
178        self
179    }
180}
181
182impl Default for ScriptOptions {
183    fn default() -> Self {
184        Self {
185            openscript_path: None,
186            working_directory: None,
187            env_vars: HashMap::new(),
188            args: Vec::new(),
189            stdin: IoOptions::Inherit,
190            stdout: IoOptions::Pipe,
191            stderr: IoOptions::Pipe,
192            timeout: None,
193            exit_on_error: true,
194            print_commands: false,
195            ai_enabled: false,
196            clear_env: false,
197        }
198    }
199}
200
201/// Result of script execution.
202///
203/// Contains all information about the completed script execution, including
204/// exit code, captured output, execution duration, and timeout status.
205///
206/// # Examples
207///
208/// ```rust
209/// use openrunner_rs::{run, ScriptOptions};
210///
211/// # #[tokio::main]
212/// # async fn main() -> openrunner_rs::Result<()> {
213/// let options = ScriptOptions::new().openscript_path("/bin/sh");
214/// let result = run("echo 'test'", options).await?;
215///
216/// println!("Exit code: {}", result.exit_code);
217/// println!("Output: {}", result.stdout);
218/// println!("Duration: {:?}", result.duration);
219///
220/// if result.timed_out {
221///     println!("Script was terminated due to timeout");
222/// }
223/// # Ok(())
224/// # }
225/// ```
226#[derive(Debug, Clone, Default)]
227pub struct ExecResult {
228    /// The exit code of the script process.
229    pub exit_code: i32,
230    /// Captured stdout from the script execution.
231    pub stdout: String,
232    /// Captured stderr from the script execution.
233    pub stderr: String,
234    /// Total duration of script execution.
235    pub duration: Duration,
236    /// Whether the script was terminated due to timeout.
237    pub timed_out: bool,
238}
239
240/// I/O redirection options for script execution.
241///
242/// Controls how stdin, stdout, and stderr are handled during script execution.
243#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
244pub enum IoOptions {
245    /// Inherit from the parent process.
246    ///
247    /// The script will use the same stdin/stdout/stderr as the calling process.
248    Inherit,
249    /// Pipe to the parent process.
250    ///
251    /// The script's I/O will be captured and made available in the `ExecResult`.
252    #[default]
253    Pipe,
254    /// Discard the output.
255    ///
256    /// The script's I/O will be redirected to /dev/null (or equivalent).
257    Null,
258}
259
260/// Result of a spawn operation.
261///
262/// This struct holds the `Child` process handle and ensures that any temporary
263/// files created for the script live as long as the handle.
264#[derive(Debug)]
265pub struct SpawnResult {
266    pub child: Child,
267    pub(crate) _temp_file: Option<NamedTempFile>,
268}