1#![forbid(unsafe_code)]
7
8use petri_artifacts_common::tags::MachineArch;
9use petri_artifacts_core::ErasedArtifactHandle;
10use std::env::consts::EXE_EXTENSION;
11use std::path::Path;
12use std::path::PathBuf;
13use vmm_test_images::KnownTestArtifacts;
14
15pub struct OpenvmmKnownPathsTestArtifactResolver<'a>(&'a str);
19
20impl<'a> OpenvmmKnownPathsTestArtifactResolver<'a> {
21 pub fn new(test_name: &'a str) -> Self {
23 Self(test_name)
24 }
25}
26
27impl petri_artifacts_core::ResolveTestArtifact for OpenvmmKnownPathsTestArtifactResolver<'_> {
28 #[rustfmt::skip]
29 fn resolve(&self, id: ErasedArtifactHandle) -> anyhow::Result<PathBuf> {
30 use petri_artifacts_common::artifacts as common;
31 use petri_artifacts_vmm_test::artifacts::*;
32
33 match id {
34 _ if id == common::PIPETTE_WINDOWS_X64 => pipette_path(MachineArch::X86_64, PipetteFlavor::Windows),
35 _ if id == common::PIPETTE_LINUX_X64 => pipette_path(MachineArch::X86_64, PipetteFlavor::Linux),
36 _ if id == common::PIPETTE_WINDOWS_AARCH64 => pipette_path(MachineArch::Aarch64, PipetteFlavor::Windows),
37 _ if id == common::PIPETTE_LINUX_AARCH64 => pipette_path(MachineArch::Aarch64, PipetteFlavor::Linux),
38
39 _ if id == common::TEST_LOG_DIRECTORY => test_log_directory_path(self.0),
40
41 _ if id == OPENVMM_NATIVE => openvmm_native_executable_path(),
42
43 _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_X64 => linux_direct_x64_test_kernel_path(),
44 _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_AARCH64 => linux_direct_arm_image_path(),
45 _ if id == loadable::LINUX_DIRECT_TEST_INITRD_X64 => linux_direct_test_initrd_path(MachineArch::X86_64),
46 _ if id == loadable::LINUX_DIRECT_TEST_INITRD_AARCH64 => linux_direct_test_initrd_path(MachineArch::Aarch64),
47
48 _ if id == loadable::PCAT_FIRMWARE_X64 => pcat_firmware_path(),
49 _ if id == loadable::SVGA_FIRMWARE_X64 => svga_firmware_path(),
50 _ if id == loadable::UEFI_FIRMWARE_X64 => uefi_firmware_path(MachineArch::X86_64),
51 _ if id == loadable::UEFI_FIRMWARE_AARCH64 => uefi_firmware_path(MachineArch::Aarch64),
52
53 _ if id == openhcl_igvm::LATEST_STANDARD_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Standard),
54 _ if id == openhcl_igvm::LATEST_STANDARD_DEV_KERNEL_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel),
55 _ if id == openhcl_igvm::LATEST_CVM_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Cvm),
56 _ if id == openhcl_igvm::LATEST_LINUX_DIRECT_TEST_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::LinuxDirect),
57 _ if id == openhcl_igvm::LATEST_STANDARD_AARCH64 => openhcl_bin_path(MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::Standard),
58 _ if id == openhcl_igvm::LATEST_STANDARD_DEV_KERNEL_AARCH64 => openhcl_bin_path(MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel),
59
60 _ if id == openhcl_igvm::RELEASE_25_05_STANDARD_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Release2505, OpenhclFlavor::Standard),
61 _ if id == openhcl_igvm::RELEASE_25_05_LINUX_DIRECT_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Release2505, OpenhclFlavor::LinuxDirect),
62 _ if id == openhcl_igvm::RELEASE_25_05_STANDARD_AARCH64 => openhcl_bin_path(MachineArch::Aarch64, OpenhclVersion::Release2505, OpenhclFlavor::Standard),
63
64 _ if id == openhcl_igvm::um_bin::LATEST_LINUX_DIRECT_TEST_X64 => openhcl_extras_path(OpenhclVersion::Latest,OpenhclFlavor::LinuxDirect,OpenhclExtras::UmBin),
65 _ if id == openhcl_igvm::um_dbg::LATEST_LINUX_DIRECT_TEST_X64 => openhcl_extras_path(OpenhclVersion::Latest,OpenhclFlavor::LinuxDirect,OpenhclExtras::UmDbg),
66
67 _ if id == test_vhd::GUEST_TEST_UEFI_X64 => guest_test_uefi_disk_path(MachineArch::X86_64),
68 _ if id == test_vhd::GUEST_TEST_UEFI_AARCH64 => guest_test_uefi_disk_path(MachineArch::Aarch64),
69 _ if id == test_vhd::GEN1_WINDOWS_DATA_CENTER_CORE2022_X64 => get_test_artifact_path(KnownTestArtifacts::Gen1WindowsDataCenterCore2022X64Vhd),
70 _ if id == test_vhd::GEN2_WINDOWS_DATA_CENTER_CORE2022_X64 => get_test_artifact_path(KnownTestArtifacts::Gen2WindowsDataCenterCore2022X64Vhd),
71 _ if id == test_vhd::GEN2_WINDOWS_DATA_CENTER_CORE2025_X64 => get_test_artifact_path(KnownTestArtifacts::Gen2WindowsDataCenterCore2025X64Vhd),
72 _ if id == test_vhd::GEN2_WINDOWS_DATA_CENTER_CORE2025_X64_PREPPED => get_prepped_test_artifact_path(KnownTestArtifacts::Gen2WindowsDataCenterCore2025X64Vhd),
73 _ if id == test_vhd::FREE_BSD_13_2_X64 => get_test_artifact_path(KnownTestArtifacts::FreeBsd13_2X64Vhd),
74 _ if id == test_vhd::UBUNTU_2404_SERVER_X64 => get_test_artifact_path(KnownTestArtifacts::Ubuntu2404ServerX64Vhd),
75 _ if id == test_vhd::UBUNTU_2504_SERVER_X64 => get_test_artifact_path(KnownTestArtifacts::Ubuntu2504ServerX64Vhd),
76 _ if id == test_vhd::UBUNTU_2404_SERVER_AARCH64 => get_test_artifact_path(KnownTestArtifacts::Ubuntu2404ServerAarch64Vhd),
77 _ if id == test_vhd::WINDOWS_11_ENTERPRISE_AARCH64 => get_test_artifact_path(KnownTestArtifacts::Windows11EnterpriseAarch64Vhdx),
78
79 _ if id == test_iso::FREE_BSD_13_2_X64 => get_test_artifact_path(KnownTestArtifacts::FreeBsd13_2X64Iso),
80
81 _ if id == test_vmgs::VMGS_WITH_BOOT_ENTRY => get_test_artifact_path(KnownTestArtifacts::VmgsWithBootEntry),
82
83 _ if id == tmks::TMK_VMM_NATIVE => tmk_vmm_native_executable_path(),
84 _ if id == tmks::TMK_VMM_LINUX_X64_MUSL => tmk_vmm_paravisor_path(MachineArch::X86_64),
85 _ if id == tmks::TMK_VMM_LINUX_AARCH64_MUSL => tmk_vmm_paravisor_path(MachineArch::Aarch64),
86 _ if id == tmks::SIMPLE_TMK_X64 => simple_tmk_path(MachineArch::X86_64),
87 _ if id == tmks::SIMPLE_TMK_AARCH64 => simple_tmk_path(MachineArch::Aarch64),
88
89 _ if id == VMGSTOOL_NATIVE => vmgstool_native_executable_path(),
90
91 _ if id == guest_tools::TPM_GUEST_TESTS_WINDOWS_X64 => {
92 tpm_guest_tests_windows_path(MachineArch::X86_64)
93 }
94 _ if id == guest_tools::TPM_GUEST_TESTS_LINUX_X64 => {
95 tpm_guest_tests_linux_path(MachineArch::X86_64)
96 }
97
98 _ => anyhow::bail!("no support for given artifact type"),
99 }
100 }
101}
102
103#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
104enum PipetteFlavor {
105 Windows,
106 Linux,
107}
108
109#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
110enum OpenhclVersion {
111 Latest,
112 Release2505,
113}
114
115#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
116enum OpenhclFlavor {
117 Standard,
118 StandardDevKernel,
119 Cvm,
120 LinuxDirect,
121}
122
123#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
124enum OpenhclExtras {
125 UmBin,
126 UmDbg,
127}
128
129fn target_arch_path(arch: MachineArch) -> &'static str {
131 match arch {
132 MachineArch::X86_64 => "x86_64",
133 MachineArch::Aarch64 => "aarch64",
134 }
135}
136
137fn windows_msvc_target(arch: MachineArch) -> &'static str {
138 match arch {
139 MachineArch::X86_64 => "x86_64-pc-windows-msvc",
140 MachineArch::Aarch64 => "aarch64-pc-windows-msvc",
141 }
142}
143
144fn get_test_artifact_path(artifact: KnownTestArtifacts) -> Result<PathBuf, anyhow::Error> {
145 let images_dir = std::env::var("VMM_TEST_IMAGES");
146 let full_path = Path::new(images_dir.as_deref().unwrap_or("images"));
147
148 get_path(
149 full_path,
150 artifact.filename(),
151 MissingCommand::Xtask {
152 xtask_args: &[
153 "guest-test",
154 "download-image",
155 "--artifacts",
156 &artifact.name(),
157 ],
158 description: "test artifact",
159 },
160 )
161}
162
163fn get_prepped_test_artifact_path(artifact: KnownTestArtifacts) -> Result<PathBuf, anyhow::Error> {
164 let images_dir = std::env::var("VMM_TEST_IMAGES");
165 let full_path = Path::new(images_dir.as_deref().unwrap_or("images"));
166 let prepped_filename = artifact.filename().replace(".vhd", "-prepped.vhd");
167
168 get_path(
169 full_path,
170 prepped_filename,
171 MissingCommand::Run {
172 description: "prepped test image",
173 package: "prep_steps",
174 },
175 )
176}
177
178fn guest_test_uefi_disk_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
180 get_path(
182 format!("target/{}-unknown-uefi/debug", target_arch_path(arch)),
183 "guest_test_uefi.img",
184 MissingCommand::Xtask {
185 xtask_args: &[
186 "guest-test",
187 "uefi",
188 &format!(
189 "--boot{}",
190 match arch {
191 MachineArch::X86_64 => "x64",
192 MachineArch::Aarch64 => "aa64",
193 }
194 ),
195 ],
196 description: "guest_test_uefi image",
197 },
198 )
199}
200
201fn pipette_path(arch: MachineArch, os_flavor: PipetteFlavor) -> anyhow::Result<PathBuf> {
203 let (target_suffixes, binary) = match os_flavor {
206 PipetteFlavor::Windows => (vec!["pc-windows-msvc", "pc-windows-gnu"], "pipette.exe"),
207 PipetteFlavor::Linux => (vec!["unknown-linux-musl"], "pipette"),
208 };
209 for (index, target_suffix) in target_suffixes.iter().enumerate() {
210 let target = format!("{}-{}", target_arch_path(arch), target_suffix);
211 match get_path(
212 format!("target/{target}/debug"),
213 binary,
214 MissingCommand::Build {
215 package: "pipette",
216 target: Some(&target),
217 },
218 ) {
219 Ok(path) => return Ok(path),
220 Err(err) => {
221 if index < target_suffixes.len() - 1 {
222 continue;
223 } else {
224 anyhow::bail!(
225 "None of the suffixes {:?} had `pipette` built, {err:?}",
226 target_suffixes
227 );
228 }
229 }
230 }
231 }
232
233 unreachable!()
234}
235
236fn openvmm_native_executable_path() -> anyhow::Result<PathBuf> {
238 get_output_executable_path("openvmm")
239}
240
241fn tmk_vmm_native_executable_path() -> anyhow::Result<PathBuf> {
243 get_output_executable_path("tmk_vmm")
244}
245
246fn vmgstool_native_executable_path() -> anyhow::Result<PathBuf> {
248 get_output_executable_path("vmgstool")
249}
250
251fn tpm_guest_tests_windows_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
253 let target = windows_msvc_target(arch);
254 get_path(
255 format!("target/{target}/debug"),
256 "tpm_guest_tests.exe",
257 MissingCommand::Build {
258 package: "tpm_guest_tests",
259 target: Some(target),
260 },
261 )
262}
263
264fn tpm_guest_tests_linux_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
265 let target = match arch {
266 MachineArch::X86_64 => "x86_64-unknown-linux-gnu",
267 MachineArch::Aarch64 => "aarch64-unknown-linux-gnu",
268 };
269
270 get_path(
271 format!("target/{target}/debug"),
272 "tpm_guest_tests",
273 MissingCommand::Build {
274 package: "tpm_guest_tests",
275 target: Some(target),
276 },
277 )
278}
279
280fn tmk_vmm_paravisor_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
281 let target = match arch {
282 MachineArch::X86_64 => "x86_64-unknown-linux-musl",
283 MachineArch::Aarch64 => "aarch64-unknown-linux-musl",
284 };
285 get_path(
286 format!("target/{target}/debug"),
287 "tmk_vmm",
288 MissingCommand::Build {
289 package: "tmk_vmm",
290 target: Some(target),
291 },
292 )
293}
294
295fn simple_tmk_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
297 let arch_str = match arch {
298 MachineArch::X86_64 => "x86_64",
299 MachineArch::Aarch64 => "aarch64",
300 };
301 let target = match arch {
302 MachineArch::X86_64 => "x86_64-unknown-none",
303 MachineArch::Aarch64 => "aarch64-minimal_rt-none",
304 };
305 get_path(
306 format!("target/{target}/debug"),
307 "simple_tmk",
308 MissingCommand::Custom {
309 description: "simple_tmk",
310 cmd: &format!(
311 "RUSTC_BOOTSTRAP=1 cargo build -p simple_tmk --config openhcl/minimal_rt/{arch_str}-config.toml"
312 ),
313 },
314 )
315}
316
317fn linux_direct_x64_test_kernel_path() -> anyhow::Result<PathBuf> {
319 get_path(
320 ".packages/underhill-deps-private",
321 "x64/vmlinux",
322 MissingCommand::Restore {
323 description: "linux direct test kernel",
324 },
325 )
326}
327
328fn linux_direct_test_initrd_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
330 get_path(
331 ".packages/underhill-deps-private",
332 format!(
333 "{}/initrd",
334 match arch {
335 MachineArch::X86_64 => "x64",
336 MachineArch::Aarch64 => "aarch64",
337 }
338 ),
339 MissingCommand::Restore {
340 description: "linux direct test initrd",
341 },
342 )
343}
344
345fn linux_direct_arm_image_path() -> anyhow::Result<PathBuf> {
347 get_path(
348 ".packages/underhill-deps-private",
349 "aarch64/Image",
350 MissingCommand::Restore {
351 description: "linux direct test kernel",
352 },
353 )
354}
355
356fn pcat_firmware_path() -> anyhow::Result<PathBuf> {
358 get_path(
359 ".packages",
360 "Microsoft.Windows.VmFirmware.Pcat.amd64fre/content/vmfirmwarepcat.dll",
361 MissingCommand::Restore {
362 description: "PCAT firmware binary",
363 },
364 )
365}
366
367fn svga_firmware_path() -> anyhow::Result<PathBuf> {
369 get_path(
370 ".packages",
371 "Microsoft.Windows.VmEmulatedDevices.amd64fre/content/VmEmulatedDevices.dll",
372 MissingCommand::Restore {
373 description: "SVGA firmware binary",
374 },
375 )
376}
377
378fn uefi_firmware_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
380 get_path(
381 ".packages",
382 match arch {
383 MachineArch::X86_64 => {
384 "hyperv.uefi.mscoreuefi.x64.RELEASE/MsvmX64/RELEASE_VS2022/FV/MSVM.fd"
385 }
386 MachineArch::Aarch64 => {
387 "hyperv.uefi.mscoreuefi.AARCH64.RELEASE/MsvmAARCH64/RELEASE_VS2022/FV/MSVM.fd"
388 }
389 },
390 MissingCommand::Restore {
391 description: "UEFI firmware binary",
392 },
393 )
394}
395
396fn openhcl_bin_path(
398 arch: MachineArch,
399 version: OpenhclVersion,
400 flavor: OpenhclFlavor,
401) -> anyhow::Result<PathBuf> {
402 let (path, name, cmd) = match (arch, version, flavor) {
403 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Standard) => (
404 "flowey-out/artifacts/build-igvm/debug/x64",
405 "openhcl-x64.bin",
406 MissingCommand::XFlowey {
407 description: "OpenHCL IGVM file",
408 xflowey_args: &["build-igvm", "x64"],
409 },
410 ),
411 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel) => (
412 "flowey-out/artifacts/build-igvm/debug/x64-devkern",
413 "openhcl-x64-devkern.bin",
414 MissingCommand::XFlowey {
415 description: "OpenHCL IGVM file",
416 xflowey_args: &["build-igvm", "x64-devkern"],
417 },
418 ),
419 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Cvm) => (
420 "flowey-out/artifacts/build-igvm/debug/x64-cvm",
421 "openhcl-x64-cvm.bin",
422 MissingCommand::XFlowey {
423 description: "OpenHCL IGVM file",
424 xflowey_args: &["build-igvm", "x64-cvm"],
425 },
426 ),
427 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::LinuxDirect) => (
428 "flowey-out/artifacts/build-igvm/debug/x64-test-linux-direct",
429 "openhcl-x64-test-linux-direct.bin",
430 MissingCommand::XFlowey {
431 description: "OpenHCL IGVM file",
432 xflowey_args: &["build-igvm", "x64-test-linux-direct"],
433 },
434 ),
435 (MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::Standard) => (
436 "flowey-out/artifacts/build-igvm/debug/aarch64",
437 "openhcl-aarch64.bin",
438 MissingCommand::XFlowey {
439 description: "OpenHCL IGVM file",
440 xflowey_args: &["build-igvm", "aarch64"],
441 },
442 ),
443 (MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel) => (
444 "flowey-out/artifacts/build-igvm/debug/aarch64-devkern",
445 "openhcl-aarch64-devkern.bin",
446 MissingCommand::XFlowey {
447 description: "OpenHCL IGVM file",
448 xflowey_args: &["build-igvm", "aarch64-devkern"],
449 },
450 ),
451 (MachineArch::X86_64, OpenhclVersion::Release2505, OpenhclFlavor::LinuxDirect) => (
452 "flowey-out/artifacts/last-release-igvm-files",
453 "release-2505-x64-direct-openhcl.bin",
454 MissingCommand::XFlowey {
455 description: "Previous OpenHCL release IGVM file",
456 xflowey_args: &["restore-packages"],
457 },
458 ),
459 (MachineArch::X86_64, OpenhclVersion::Release2505, OpenhclFlavor::Standard) => (
460 "flowey-out/artifacts/last-release-igvm-files",
461 "release-2505-x64-openhcl.bin",
462 MissingCommand::XFlowey {
463 description: "Previous OpenHCL release IGVM file",
464 xflowey_args: &["restore-packages"],
465 },
466 ),
467 (MachineArch::Aarch64, OpenhclVersion::Release2505, OpenhclFlavor::Standard) => (
468 "flowey-out/artifacts/last-release-igvm-files",
469 "release-2505-aarch64-openhcl.bin",
470 MissingCommand::XFlowey {
471 description: "Previous OpenHCL release IGVM file",
472 xflowey_args: &["restore-packages"],
473 },
474 ),
475 _ => anyhow::bail!("no openhcl bin with given arch, version, and flavor"),
476 };
477
478 get_path(path, name, cmd)
479}
480
481fn openhcl_extras_path(
483 version: OpenhclVersion,
484 flavor: OpenhclFlavor,
485 item: OpenhclExtras,
486) -> anyhow::Result<PathBuf> {
487 if !matches!(version, OpenhclVersion::Latest) || !matches!(flavor, OpenhclFlavor::LinuxDirect) {
488 anyhow::bail!("Debug symbol path currently only available for LATEST_LINUX_DIRECT_TEST")
489 }
490
491 let (path, name) = match item {
492 OpenhclExtras::UmBin => (
493 "flowey-out/artifacts/build-igvm/debug/x64-test-linux-direct",
494 "openvmm_hcl_msft",
495 ),
496 OpenhclExtras::UmDbg => (
497 "flowey-out/artifacts/build-igvm/debug/x64-test-linux-direct",
498 "openvmm_hcl_msft.dbg",
499 ),
500 };
501
502 get_path(
503 path,
504 name,
505 MissingCommand::XFlowey {
506 description: "OpenHCL IGVM file",
507 xflowey_args: &["build-igvm", "x64-test-linux-direct"],
508 },
509 )
510}
511
512fn test_log_directory_path(test_name: &str) -> anyhow::Result<PathBuf> {
514 let root = if let Some(path) = std::env::var_os("TEST_OUTPUT_PATH") {
515 PathBuf::from(path)
516 } else {
517 get_repo_root()?.join("vmm_test_results")
518 };
519 let path = root.join(test_name.replace("::", "__"));
522 fs_err::create_dir_all(&path)?;
523 Ok(path)
524}
525
526const VMM_TESTS_DIR_ENV_VAR: &str = "VMM_TESTS_CONTENT_DIR";
527
528pub fn get_repo_root() -> anyhow::Result<PathBuf> {
530 Ok(Path::new(env!("CARGO_MANIFEST_DIR")).join("../.."))
531}
532
533pub fn get_path(
547 search_path: impl AsRef<Path>,
548 file_name: impl AsRef<Path>,
549 missing_cmd: MissingCommand<'_>,
550) -> anyhow::Result<PathBuf> {
551 let search_path = search_path.as_ref();
552 let file_name = file_name.as_ref();
553 if file_name.is_absolute() {
554 anyhow::bail!("{} should be a relative path", file_name.display());
555 }
556
557 if let Ok(env_dir) = std::env::var(VMM_TESTS_DIR_ENV_VAR) {
558 let full_path = Path::new(&env_dir).join(file_name);
559 if full_path.try_exists()? {
560 return Ok(full_path);
561 }
562 }
563
564 let file_path = if search_path.is_absolute() {
565 search_path.to_owned()
566 } else {
567 get_repo_root()?.join(search_path)
568 };
569
570 let full_path = file_path.join(file_name);
571 if !full_path.exists() {
572 eprintln!("Failed to find {:?}.", full_path);
573 missing_cmd.to_error()?;
574 }
575
576 Ok(full_path)
577}
578
579pub fn get_output_executable_path(name: &str) -> anyhow::Result<PathBuf> {
583 let mut path: PathBuf = std::env::current_exe()?;
584 if path.parent().and_then(|x| x.file_name()).unwrap() == "deps" {
587 path.pop();
588 }
589
590 get_path(
591 path.parent().unwrap(),
592 Path::new(name).with_extension(EXE_EXTENSION),
593 MissingCommand::Build {
594 package: name,
595 target: None,
596 },
597 )
598}
599
600#[derive(Copy, Clone)]
603#[expect(missing_docs)] pub enum MissingCommand<'a> {
605 Build {
607 package: &'a str,
608 target: Option<&'a str>,
609 },
610 Run {
612 description: &'a str,
613 package: &'a str,
614 },
615 Xtask {
617 description: &'a str,
618 xtask_args: &'a [&'a str],
619 },
620 XFlowey {
622 description: &'a str,
623 xflowey_args: &'a [&'a str],
624 },
625 Restore { description: &'a str },
627 Custom { description: &'a str, cmd: &'a str },
629}
630
631impl MissingCommand<'_> {
632 fn to_error(self) -> anyhow::Result<()> {
633 match self {
634 MissingCommand::Build { package, target } => anyhow::bail!(
635 "Failed to find {package} binary. Run `cargo build {target_args}-p {package}` to build it.",
636 target_args =
637 target.map_or(String::new(), |target| format!("--target {} ", target)),
638 ),
639 MissingCommand::Run {
640 description,
641 package,
642 } => anyhow::bail!(
643 "Failed to find {}. Run `cargo run -p {}` to create it.",
644 description,
645 package
646 ),
647 MissingCommand::Xtask {
648 description,
649 xtask_args: args,
650 } => {
651 anyhow::bail!(
652 "Failed to find {}. Run `cargo xtask {}` to create it.",
653 description,
654 args.join(" ")
655 )
656 }
657 MissingCommand::XFlowey {
658 description,
659 xflowey_args: args,
660 } => anyhow::bail!(
661 "Failed to find {}. Run `cargo xflowey {}` to create it.",
662 description,
663 args.join(" ")
664 ),
665 MissingCommand::Restore { description } => {
666 anyhow::bail!(
667 "Failed to find {}. Run `cargo xflowey restore-packages`.",
668 description
669 )
670 }
671 MissingCommand::Custom { description, cmd } => {
672 anyhow::bail!(
673 "Failed to find {}. Run `{}` to create it.",
674 description,
675 cmd
676 )
677 }
678 }
679 }
680}