1#![allow(clippy::new_without_default)]
2
3pub mod error;
4pub mod helper;
5pub mod pkgs;
6pub mod types;
7
8use globset::{Glob, GlobSet, GlobSetBuilder};
9pub use pkgs::common::model::{DependentPackage, Package, PackageManifest};
10use std::collections::HashMap;
11use std::path::Path;
12use std::sync::Arc;
13use crate::types::SupportedType;
14
15
16#[derive(Clone)]
17pub struct Scanner {
18
19 scanners: Vec<Arc<dyn PackageManifest + Send + Sync>>,
20 types: Vec<SupportedType>,
21
22 glob_index_to_scanner_index: HashMap<usize, usize>,
23 glob_set: GlobSet,
24}
25
26impl Scanner {
27 pub fn new() -> Self {
28 let scanners = pkgs::create_scanners();
29
30 let mut glob_index_to_scanner_index = HashMap::new();
31 let mut glob_set = GlobSetBuilder::new();
32 let mut current_glob_index = 0usize;
33
34 let mut supported_types = vec![];
35 for (index, scanner) in scanners.iter().enumerate() {
36 let supported_type = SupportedType {
37 name: scanner.get_identifier(),
38 filenames: scanner
39 .file_name_patterns()
40 .iter()
41 .map(|s| s.to_string())
42 .collect(),
43 patterns: vec![],
44 };
45 supported_types.push(supported_type);
46
47 for pat in scanner.file_name_patterns() {
48 let glob = Glob::new(pat).expect("Failed to compile pattern");
49 glob_set.add(glob);
50 glob_index_to_scanner_index.insert(current_glob_index, index);
51 current_glob_index += 1;
52 }
53 }
54
55 let glob_set = glob_set.build().expect("Failed to build glob set");
56
57 Self {
58 scanners,
59 types: supported_types,
60
61 glob_index_to_scanner_index,
62 glob_set,
63 }
64 }
65
66 pub async fn scan(
67 &self,
68 path: impl AsRef<Path>,
69 prefix: impl AsRef<Path>,
70 ) -> Result<(String, Package), error::SourcePkgError> {
71 let location = path.as_ref();
72 let prefix = prefix.as_ref();
73
74 if let Some(match_idx) = self
75 .glob_set
76 .matches(
77 location
78 .file_name()
79 .ok_or(error::SourcePkgError::GenericsError(
80 "Invalid file name ending in '..'",
81 ))?,
82 )
83 .first()
84 {
85 let scanner_idx = self.glob_index_to_scanner_index[match_idx];
86 let scanner = &self.scanners[scanner_idx];
87 let ctx = pkgs::RecognizeContext {
88 prefix: prefix.to_path_buf(),
89 };
90
91 return match scanner.recognize_with_config(location, &ctx).await {
92 Ok(manifest) => Ok((scanner.get_name(), manifest)),
93 Err(e) => Err(e),
94 };
95 }
96
97 Err(error::SourcePkgError::NotSupported)
98 }
99
100 pub fn supported_types(&self) -> &[SupportedType] {
101 &self.types
102 }
103}