Skip to main content

cargo_image_runner/runner/
mod.rs

1//! Runner trait and QEMU implementation for executing bootable images.
2
3use crate::core::context::Context;
4use crate::core::error::Result;
5use std::path::Path;
6
7// Runner implementations
8#[cfg(feature = "qemu")]
9pub mod qemu;
10
11/// Runner trait for executing images.
12pub trait Runner: Send + Sync {
13    /// Execute the image.
14    ///
15    /// Returns information about the run result.
16    fn run(&self, ctx: &Context, image_path: &Path) -> Result<RunResult>;
17
18    /// Check if the runner is available on the system.
19    fn is_available(&self) -> bool;
20
21    /// Validate runner configuration.
22    fn validate(&self, ctx: &Context) -> Result<()> {
23        if !self.is_available() {
24            return Err(crate::core::error::Error::runner(format!(
25                "{} is not available on this system",
26                self.name()
27            )));
28        }
29
30        let _ = ctx;
31        Ok(())
32    }
33
34    /// Get a human-readable name for this runner.
35    fn name(&self) -> &str;
36}
37
38/// Result of running an image.
39#[derive(Debug)]
40pub struct RunResult {
41    /// Exit code from the runner.
42    pub exit_code: i32,
43
44    /// Whether the run was considered successful.
45    pub success: bool,
46
47    /// Captured stdout/stderr output (populated in test mode).
48    pub captured_output: Option<CapturedOutput>,
49
50    /// Whether the run was terminated due to a timeout.
51    pub timed_out: bool,
52}
53
54/// Captured stdout and stderr from a runner execution.
55#[derive(Debug, Clone)]
56pub struct CapturedOutput {
57    pub stdout: String,
58    pub stderr: String,
59}
60
61impl RunResult {
62    /// Create a new run result.
63    pub fn new(exit_code: i32, success: bool) -> Self {
64        Self {
65            exit_code,
66            success,
67            captured_output: None,
68            timed_out: false,
69        }
70    }
71
72    /// Create a successful result with exit code 0.
73    pub fn success() -> Self {
74        Self {
75            exit_code: 0,
76            success: true,
77            captured_output: None,
78            timed_out: false,
79        }
80    }
81
82    /// Create a failed result with the given exit code.
83    pub fn failed(exit_code: i32) -> Self {
84        Self {
85            exit_code,
86            success: false,
87            captured_output: None,
88            timed_out: false,
89        }
90    }
91
92    /// Attach captured output to the result.
93    pub fn with_output(mut self, stdout: String, stderr: String) -> Self {
94        self.captured_output = Some(CapturedOutput { stdout, stderr });
95        self
96    }
97
98    /// Mark the result as timed out.
99    pub fn with_timeout(mut self) -> Self {
100        self.timed_out = true;
101        self
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn test_run_result_success() {
111        let result = RunResult::success();
112        assert_eq!(result.exit_code, 0);
113        assert!(result.success);
114    }
115
116    #[test]
117    fn test_run_result_failed() {
118        let result = RunResult::failed(1);
119        assert_eq!(result.exit_code, 1);
120        assert!(!result.success);
121    }
122
123    #[test]
124    fn test_run_result_custom() {
125        let result = RunResult::new(33, true);
126        assert_eq!(result.exit_code, 33);
127        assert!(result.success);
128    }
129}