cargo_image_runner/core/
context.rs1use crate::config::Config;
2use crate::core::error::Result;
3use std::collections::HashMap;
4use std::path::PathBuf;
5
6pub struct Context {
8 pub config: Config,
10
11 pub workspace_root: PathBuf,
13
14 pub target_dir: PathBuf,
16
17 pub executable: PathBuf,
19
20 pub is_test: bool,
22
23 pub cache_dir: PathBuf,
25
26 pub output_dir: PathBuf,
28
29 pub template_vars: HashMap<String, String>,
31}
32
33impl Context {
34 pub fn new(config: Config, workspace_root: PathBuf, executable: PathBuf) -> Result<Self> {
36 let target_dir = workspace_root.join("target").join("image-runner");
37 let cache_dir = target_dir.join("cache");
38 let output_dir = target_dir.join("output");
39
40 std::fs::create_dir_all(&cache_dir)?;
42 std::fs::create_dir_all(&output_dir)?;
43
44 let mut ctx = Self {
45 config,
46 workspace_root: workspace_root.clone(),
47 target_dir,
48 executable: executable.clone(),
49 is_test: false,
50 cache_dir,
51 output_dir,
52 template_vars: HashMap::new(),
53 };
54
55 ctx.detect_test();
57
58 ctx.init_template_vars();
60
61 Ok(ctx)
62 }
63
64 pub fn detect_test(&mut self) {
70 if let Some(stem) = self.executable.file_stem().and_then(|n| n.to_str()) {
71 if stem.contains('-') {
74 if let Some(suffix) = stem.rsplit('-').next() {
75 if suffix.len() >= 8 && suffix.chars().all(|c| c.is_ascii_hexdigit()) {
77 self.is_test = true;
78 }
79 }
80 }
81 }
82 }
83
84 fn init_template_vars(&mut self) {
86 self.template_vars = self.config.variables.clone();
88
89 self.template_vars.insert(
91 "EXECUTABLE".to_string(),
92 self.executable.display().to_string(),
93 );
94
95 if let Some(exe_name) = self.executable.file_name().and_then(|n| n.to_str()) {
96 self.template_vars
97 .insert("EXECUTABLE_NAME".to_string(), exe_name.to_string());
98 }
99
100 self.template_vars.insert(
101 "WORKSPACE_ROOT".to_string(),
102 self.workspace_root.display().to_string(),
103 );
104
105 self.template_vars.insert(
106 "OUTPUT_DIR".to_string(),
107 self.output_dir.display().to_string(),
108 );
109
110 self.template_vars.insert(
111 "IS_TEST".to_string(),
112 if self.is_test { "1" } else { "0" }.to_string(),
113 );
114 }
115
116 pub fn get_extra_args(&self) -> &[String] {
118 if self.is_test {
119 &self.config.test.extra_args
120 } else {
121 &self.config.run.extra_args
122 }
123 }
124
125 pub fn test_success_exit_code(&self) -> Option<i32> {
127 self.config.test.success_exit_code
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134 use crate::config::Config;
135
136 fn make_context(workspace: &std::path::Path, exe: &std::path::Path) -> Context {
137 Context::new(Config::default(), workspace.to_path_buf(), exe.to_path_buf()).unwrap()
138 }
139
140 #[test]
141 fn test_context_paths() {
142 let dir = tempfile::tempdir().unwrap();
143 let exe = dir.path().join("my-kernel");
144 std::fs::write(&exe, b"fake").unwrap();
145
146 let ctx = make_context(dir.path(), &exe);
147 assert_eq!(ctx.target_dir, dir.path().join("target/image-runner"));
148 assert_eq!(ctx.cache_dir, dir.path().join("target/image-runner/cache"));
149 assert_eq!(
150 ctx.output_dir,
151 dir.path().join("target/image-runner/output")
152 );
153 }
154
155 #[test]
156 fn test_builtin_template_vars() {
157 let dir = tempfile::tempdir().unwrap();
158 let exe = dir.path().join("my-kernel");
159 std::fs::write(&exe, b"fake").unwrap();
160
161 let ctx = make_context(dir.path(), &exe);
162 assert_eq!(
163 ctx.template_vars.get("EXECUTABLE").unwrap(),
164 &exe.display().to_string()
165 );
166 assert_eq!(
167 ctx.template_vars.get("EXECUTABLE_NAME").unwrap(),
168 "my-kernel"
169 );
170 assert_eq!(
171 ctx.template_vars.get("WORKSPACE_ROOT").unwrap(),
172 &dir.path().display().to_string()
173 );
174 assert_eq!(
175 ctx.template_vars.get("OUTPUT_DIR").unwrap(),
176 &ctx.output_dir.display().to_string()
177 );
178 assert_eq!(ctx.template_vars.get("IS_TEST").unwrap(), "0");
179 }
180
181 #[test]
182 fn test_user_variables_included() {
183 let dir = tempfile::tempdir().unwrap();
184 let exe = dir.path().join("my-kernel");
185 std::fs::write(&exe, b"fake").unwrap();
186
187 let mut config = Config::default();
188 config
189 .variables
190 .insert("MY_VAR".to_string(), "hello".to_string());
191
192 let ctx =
193 Context::new(config, dir.path().to_path_buf(), exe).unwrap();
194 assert_eq!(ctx.template_vars.get("MY_VAR").unwrap(), "hello");
195 }
196
197 #[test]
198 fn test_detect_test_with_hash_suffix() {
199 let dir = tempfile::tempdir().unwrap();
200 let exe = dir.path().join("my-test-a1b2c3d4e5f6a7b8");
201 std::fs::write(&exe, b"fake").unwrap();
202
203 let ctx = make_context(dir.path(), &exe);
204 assert!(ctx.is_test);
205 assert_eq!(ctx.template_vars.get("IS_TEST").unwrap(), "1");
206 }
207
208 #[test]
209 fn test_detect_test_with_efi_extension() {
210 let dir = tempfile::tempdir().unwrap();
211 let exe = dir.path().join("basic_boot-edba05eea98a559f.efi");
212 std::fs::write(&exe, b"fake").unwrap();
213
214 let ctx = make_context(dir.path(), &exe);
215 assert!(ctx.is_test);
216 assert_eq!(ctx.template_vars.get("IS_TEST").unwrap(), "1");
217 }
218
219 #[test]
220 fn test_detect_normal_executable() {
221 let dir = tempfile::tempdir().unwrap();
222 let exe = dir.path().join("my-kernel");
223 std::fs::write(&exe, b"fake").unwrap();
224
225 let ctx = make_context(dir.path(), &exe);
226 assert!(!ctx.is_test);
227 }
228
229 #[test]
230 fn test_get_extra_args_test_mode() {
231 let dir = tempfile::tempdir().unwrap();
232 let exe = dir.path().join("my-test-a1b2c3d4e5f6a7b8");
233 std::fs::write(&exe, b"fake").unwrap();
234
235 let mut config = Config::default();
236 config.test.extra_args = vec!["-device".to_string(), "isa-debug-exit".to_string()];
237 config.run.extra_args = vec!["-serial".to_string(), "stdio".to_string()];
238
239 let ctx =
240 Context::new(config, dir.path().to_path_buf(), exe).unwrap();
241 assert!(ctx.is_test);
242 assert_eq!(ctx.get_extra_args(), &["-device", "isa-debug-exit"]);
243 }
244
245 #[test]
246 fn test_get_extra_args_run_mode() {
247 let dir = tempfile::tempdir().unwrap();
248 let exe = dir.path().join("my-kernel");
249 std::fs::write(&exe, b"fake").unwrap();
250
251 let mut config = Config::default();
252 config.test.extra_args = vec!["-device".to_string(), "isa-debug-exit".to_string()];
253 config.run.extra_args = vec!["-serial".to_string(), "stdio".to_string()];
254
255 let ctx =
256 Context::new(config, dir.path().to_path_buf(), exe).unwrap();
257 assert!(!ctx.is_test);
258 assert_eq!(ctx.get_extra_args(), &["-serial", "stdio"]);
259 }
260
261 #[test]
262 fn test_success_exit_code() {
263 let dir = tempfile::tempdir().unwrap();
264 let exe = dir.path().join("my-kernel");
265 std::fs::write(&exe, b"fake").unwrap();
266
267 let mut config = Config::default();
268 config.test.success_exit_code = Some(33);
269
270 let ctx =
271 Context::new(config, dir.path().to_path_buf(), exe).unwrap();
272 assert_eq!(ctx.test_success_exit_code(), Some(33));
273 }
274}