verifyos_cli/parsers/
bundle_scanner.rs1use std::fs;
2use std::path::{Path, PathBuf};
3
4#[derive(Debug, thiserror::Error)]
5pub enum BundleScanError {
6 #[error("IO Error: {0}")]
7 Io(#[from] std::io::Error),
8}
9
10#[derive(Debug, Clone)]
11pub struct BundleTarget {
12 pub bundle_path: PathBuf,
13 pub display_name: String,
14}
15
16pub fn find_nested_bundles(app_bundle_path: &Path) -> Result<Vec<BundleTarget>, BundleScanError> {
17 let mut bundles = Vec::new();
18
19 let frameworks = app_bundle_path.join("Frameworks");
20 collect_bundles_in_dir(&frameworks, &mut bundles)?;
21
22 let plugins = app_bundle_path.join("PlugIns");
23 collect_bundles_in_dir(&plugins, &mut bundles)?;
24
25 let extensions = app_bundle_path.join("Extensions");
26 collect_bundles_in_dir(&extensions, &mut bundles)?;
27
28 bundles.sort_by(|a, b| a.bundle_path.cmp(&b.bundle_path));
29 bundles.dedup_by(|a, b| a.bundle_path == b.bundle_path);
30
31 Ok(bundles)
32}
33
34fn collect_bundles_in_dir(
35 dir: &Path,
36 bundles: &mut Vec<BundleTarget>,
37) -> Result<(), BundleScanError> {
38 if !dir.exists() {
39 return Ok(());
40 }
41
42 for entry in fs::read_dir(dir)? {
43 let entry = entry?;
44 let path = entry.path();
45
46 if path.extension().and_then(|e| e.to_str()) == Some("app")
47 || path.extension().and_then(|e| e.to_str()) == Some("appex")
48 || path.extension().and_then(|e| e.to_str()) == Some("framework")
49 {
50 bundles.push(BundleTarget {
51 display_name: path
52 .file_name()
53 .and_then(|n| n.to_str())
54 .unwrap_or("")
55 .to_string(),
56 bundle_path: path.clone(),
57 });
58 }
59
60 if path.is_dir() {
61 collect_bundles_in_dir(&path, bundles)?;
62 }
63 }
64
65 Ok(())
66}