Skip to main content

verifyos_cli/parsers/
bundle_scanner.rs

1use 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}