mimobox_sdk/types.rs
1#[cfg(all(feature = "vm", target_os = "linux"))]
2use crate::config::Config;
3use crate::error::SdkError;
4#[cfg(all(feature = "vm", target_os = "linux"))]
5use crate::vm_helpers::map_microvm_error;
6use crate::vm_helpers::{map_pty_session_error, map_snapshot_bytes_error};
7use mimobox_core::{PtyEvent, PtySize, SandboxResult};
8use std::path::{Path, PathBuf};
9#[cfg(all(feature = "vm", target_os = "linux"))]
10use std::sync::Arc;
11use std::sync::mpsc;
12
13/// Result of a sandbox command execution.
14///
15/// Contains raw stdout/stderr bytes, exit code, timeout flag, and wall-clock elapsed time.
16/// Use `String::from_utf8_lossy()` to convert stdout/stderr to text.
17///
18/// # Examples
19///
20/// ```rust,no_run
21/// use mimobox_sdk::Sandbox;
22///
23/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
24/// let mut sandbox = Sandbox::new()?;
25/// let result = sandbox.execute("/bin/echo hello")?;
26/// println!("exit: {:?}", result.exit_code);
27/// println!("stdout: {}", String::from_utf8_lossy(&result.stdout));
28/// println!("elapsed: {:?}", result.elapsed);
29/// # Ok(())
30/// # }
31/// ```
32#[non_exhaustive]
33pub struct ExecuteResult {
34 /// Standard output byte stream.
35 pub stdout: Vec<u8>,
36 /// Standard error byte stream.
37 pub stderr: Vec<u8>,
38 /// Exit code; may be `None` if the process did not exit normally.
39 pub exit_code: Option<i32>,
40 /// Whether execution was terminated due to timeout.
41 pub timed_out: bool,
42 /// Total elapsed time for this execution.
43 pub elapsed: std::time::Duration,
44}
45
46impl ExecuteResult {
47 /// Constructs a new execution result.
48 ///
49 /// Because `ExecuteResult` is marked `#[non_exhaustive]`, external crates
50 /// cannot construct it with a struct literal.
51 /// This method provides a stable construction path.
52 pub fn new(
53 stdout: Vec<u8>,
54 stderr: Vec<u8>,
55 exit_code: Option<i32>,
56 timed_out: bool,
57 elapsed: std::time::Duration,
58 ) -> Self {
59 Self {
60 stdout,
61 stderr,
62 exit_code,
63 timed_out,
64 elapsed,
65 }
66 }
67}
68
69/// HTTP response from the controlled host-side proxy.
70///
71/// Returned by [`Sandbox::http_request()`]. Only available with the `vm` feature on Linux.
72///
73/// # Examples
74///
75/// ```rust,no_run,ignore
76/// // Requires `vm` feature + Linux
77/// use mimobox_sdk::{Config, IsolationLevel, Sandbox};
78/// use std::collections::HashMap;
79///
80/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
81/// let config = Config::builder()
82/// .isolation(IsolationLevel::MicroVm)
83/// .allowed_http_domains(["example.com"])
84/// .build();
85/// let mut sandbox = Sandbox::with_config(config)?;
86/// let resp = sandbox.http_request("GET", "https://example.com", HashMap::new(), None)?;
87/// println!("status: {}", resp.status);
88/// # Ok(())
89/// # }
90/// ```
91#[non_exhaustive]
92pub struct HttpResponse {
93 /// HTTP status code.
94 pub status: u16,
95 /// Normalized response headers.
96 pub headers: std::collections::HashMap<String, String>,
97 /// Response body byte stream.
98 pub body: Vec<u8>,
99}
100
101/// Opaque handle to a sandbox memory snapshot.
102///
103/// Supports both in-memory bytes and file-backed storage modes.
104/// Create via [`Sandbox::snapshot()`] or restore from bytes/file.
105///
106/// # Examples
107///
108/// ```rust,no_run
109/// use mimobox_sdk::{Config, IsolationLevel, Sandbox};
110///
111/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
112/// let config = Config::builder()
113/// .isolation(IsolationLevel::MicroVm)
114/// .build();
115/// let mut sandbox = Sandbox::with_config(config)?;
116/// let snapshot = sandbox.snapshot()?;
117/// println!("snapshot size: {} bytes", snapshot.size());
118/// # Ok(())
119/// # }
120/// ```
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub struct SandboxSnapshot {
123 pub(crate) inner: mimobox_core::SandboxSnapshot,
124}
125
126impl SandboxSnapshot {
127 /// Restores a snapshot from raw bytes.
128 pub fn from_bytes(data: &[u8]) -> Result<Self, SdkError> {
129 mimobox_core::SandboxSnapshot::from_bytes(data)
130 .map(Self::from_core)
131 .map_err(map_snapshot_bytes_error)
132 }
133
134 /// Creates a snapshot from a file-backed snapshot's `memory.bin`.
135 pub fn from_file(path: PathBuf) -> Result<Self, SdkError> {
136 mimobox_core::SandboxSnapshot::from_file(path)
137 .map(Self::from_core)
138 .map_err(map_snapshot_bytes_error)
139 }
140
141 /// Returns the memory file path for a file-backed snapshot.
142 pub fn memory_file_path(&self) -> Option<&Path> {
143 self.inner.memory_file_path()
144 }
145
146 /// Returns a snapshot byte slice without an extra copy.
147 ///
148 /// Only in-memory snapshots support this operation; file-backed snapshots return an error.
149 pub fn as_bytes(&self) -> Result<&[u8], SdkError> {
150 self.inner.as_bytes().map_err(map_snapshot_bytes_error)
151 }
152
153 /// Returns a copied snapshot byte buffer.
154 ///
155 /// For file-backed snapshots, rebuilds the complete self-describing snapshot bytes.
156 pub fn to_bytes(&self) -> Result<Vec<u8>, SdkError> {
157 #[cfg(all(feature = "vm", target_os = "linux"))]
158 if let Some(memory_path) = self.inner.memory_file_path() {
159 return mimobox_vm::MicrovmSnapshot::from_memory_file(memory_path)
160 .and_then(|snapshot| snapshot.snapshot())
161 .map_err(map_microvm_error);
162 }
163
164 self.inner.to_bytes().map_err(map_snapshot_bytes_error)
165 }
166
167 /// Consumes the snapshot and returns the underlying bytes without an extra copy.
168 ///
169 /// For file-backed snapshots, rebuilds the complete self-describing snapshot bytes.
170 pub fn into_bytes(self) -> Result<Vec<u8>, SdkError> {
171 #[cfg(all(feature = "vm", target_os = "linux"))]
172 if let Some(memory_path) = self.inner.memory_file_path().map(Path::to_path_buf) {
173 return mimobox_vm::MicrovmSnapshot::from_memory_file(&memory_path)
174 .and_then(|snapshot| snapshot.snapshot())
175 .map_err(map_microvm_error);
176 }
177
178 self.inner.into_bytes().map_err(map_snapshot_bytes_error)
179 }
180
181 /// Returns the snapshot size in bytes.
182 pub fn size(&self) -> usize {
183 self.inner.size()
184 }
185
186 pub(crate) fn from_core(inner: mimobox_core::SandboxSnapshot) -> Self {
187 Self { inner }
188 }
189}
190
191#[cfg(all(feature = "vm", target_os = "linux"))]
192#[derive(Debug, Clone)]
193/// Configuration for creating a snapshot-based restore pool.
194///
195/// Requires `vm` feature + Linux.
196pub struct RestorePoolConfig {
197 /// Target restore pool size.
198 pub pool_size: usize,
199 /// Base configuration used by the restore pool.
200 pub base_config: Config,
201}
202
203#[cfg(all(feature = "vm", target_os = "linux"))]
204#[derive(Clone)]
205/// Snapshot-based restore pool for sub-millisecond VM restore-to-ready latency.
206///
207/// Pre-creates a pool of empty microVM shells. When a sandbox is needed,
208/// a snapshot is restored into one of the pre-warmed shells, avoiding
209/// the full VM boot sequence.
210///
211/// Requires `vm` feature + Linux.
212pub struct RestorePool {
213 pub(crate) inner: Arc<mimobox_vm::RestorePool>,
214}
215
216impl From<SandboxResult> for ExecuteResult {
217 fn from(r: SandboxResult) -> Self {
218 Self {
219 stdout: r.stdout,
220 stderr: r.stderr,
221 exit_code: r.exit_code,
222 timed_out: r.timed_out,
223 elapsed: r.elapsed,
224 }
225 }
226}
227
228#[cfg(feature = "vm")]
229impl From<mimobox_vm::HttpResponse> for HttpResponse {
230 fn from(value: mimobox_vm::HttpResponse) -> Self {
231 Self {
232 status: value.status,
233 headers: value.headers,
234 body: value.body,
235 }
236 }
237}
238
239/// Streaming output event from [`Sandbox::stream_execute()`].
240///
241/// Events are delivered via a `std::sync::mpsc::Receiver`. `Exit` or `TimedOut`
242/// is always the last event in the stream.
243///
244/// Requires `vm` feature + Linux.
245///
246/// # Examples
247///
248/// ```rust,no_run,ignore
249/// // Requires `vm` feature + Linux
250/// use mimobox_sdk::{Config, IsolationLevel, Sandbox, StreamEvent};
251///
252/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
253/// let config = Config::builder()
254/// .isolation(IsolationLevel::MicroVm)
255/// .build();
256/// let mut sandbox = Sandbox::with_config(config)?;
257/// let rx = sandbox.stream_execute("/bin/echo hello")?;
258/// for event in rx.iter() {
259/// match event {
260/// StreamEvent::Stdout(data) => print!("{}", String::from_utf8_lossy(&data)),
261/// StreamEvent::Stderr(data) => eprint!("{}", String::from_utf8_lossy(&data)),
262/// StreamEvent::Exit(code) => println!("exit = {code}"),
263/// StreamEvent::TimedOut => println!("timed out"),
264/// _ => {}
265/// }
266/// }
267/// # Ok(())
268/// # }
269/// ```
270#[non_exhaustive]
271#[derive(Debug, Clone, PartialEq, Eq)]
272pub enum StreamEvent {
273 /// A chunk of standard output data.
274 Stdout(Vec<u8>),
275 /// A chunk of standard error data.
276 Stderr(Vec<u8>),
277 /// The process exited with an exit code.
278 Exit(i32),
279 /// Execution was terminated due to timeout.
280 TimedOut,
281}
282
283/// Interactive PTY terminal session.
284///
285/// Wraps a backend-provided terminal handle with a unified interface for
286/// input, resize, output events, and lifecycle control.
287///
288/// Currently supported on OS-level backends only (not microVM).
289///
290/// # Examples
291///
292/// ```rust,no_run
293/// use mimobox_sdk::Sandbox;
294///
295/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
296/// let mut sandbox = Sandbox::new()?;
297/// let mut pty = sandbox.create_pty("/bin/sh")?;
298/// pty.send_input(b"echo hello\n")?;
299/// let exit_code = pty.wait()?;
300/// println!("exited with {exit_code}");
301/// # Ok(())
302/// # }
303/// ```
304#[non_exhaustive]
305pub struct PtySession {
306 inner: Box<dyn mimobox_core::PtySession>,
307}
308
309impl std::fmt::Debug for PtySession {
310 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311 f.debug_struct("PtySession").finish_non_exhaustive()
312 }
313}
314
315impl PtySession {
316 /// Constructs an SDK PTY wrapper from a backend PTY session.
317 pub(crate) fn from_inner(inner: Box<dyn mimobox_core::PtySession>) -> Self {
318 Self { inner }
319 }
320
321 /// Writes input data to the PTY session.
322 pub fn send_input(&mut self, data: &[u8]) -> Result<(), SdkError> {
323 self.inner.send_input(data).map_err(map_pty_session_error)
324 }
325
326 /// Resizes the PTY terminal.
327 pub fn resize(&mut self, cols: u16, rows: u16) -> Result<(), SdkError> {
328 self.inner
329 .resize(PtySize { cols, rows })
330 .map_err(map_pty_session_error)
331 }
332
333 /// Returns the PTY output event receiver.
334 pub fn output(&self) -> &mpsc::Receiver<PtyEvent> {
335 self.inner.output_rx()
336 }
337
338 /// Forcefully terminates the PTY session.
339 pub fn kill(&mut self) -> Result<(), SdkError> {
340 self.inner.kill().map_err(map_pty_session_error)
341 }
342
343 /// Waits for the PTY process to exit and returns its exit code.
344 pub fn wait(&mut self) -> Result<i32, SdkError> {
345 self.inner.wait().map_err(map_pty_session_error)
346 }
347}