daipendency/
extractors.rs1use std::path::Path;
2
3use crate::languages::{Language, LanguageConfig};
4use crate::library::BoxedExtractor;
5use daipendency_extractor::{LibraryMetadata, LibraryMetadataError};
6use thiserror::Error;
7
8pub fn get_extractor(language: Language) -> BoxedExtractor {
9 let config = LanguageConfig::get_from_language(language);
10
11 (config.extractor_initialiser)()
12}
13
14pub struct ExtractorDiscovery {
15 pub language: Language,
16 pub extractor: BoxedExtractor,
17 pub library_metadata: LibraryMetadata,
18}
19
20impl std::fmt::Debug for ExtractorDiscovery {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 f.debug_struct("ExtractorDiscovery")
23 .field("language", &self.language)
24 .field("extractor", &"<dyn Extractor>")
25 .field("library_metadata", &self.library_metadata)
26 .finish()
27 }
28}
29
30#[derive(Error, Debug)]
31pub enum ExtractorDiscoveryError {
32 #[error("Malformed manifest for {language:?}: {error}")]
33 MalformedManifest { language: Language, error: String },
34 #[error("No matching extractor found")]
35 NotFound,
36}
37
38pub fn discover_extractor(path: &Path) -> Result<ExtractorDiscovery, ExtractorDiscoveryError> {
39 for language in LanguageConfig::get_all().keys() {
40 let extractor = get_extractor(*language);
41 match extractor.get_library_metadata(path) {
42 Ok(library_metadata) => {
43 return Ok(ExtractorDiscovery {
44 language: *language,
45 extractor,
46 library_metadata,
47 })
48 }
49 Err(LibraryMetadataError::MalformedManifest(error)) => {
50 return Err(ExtractorDiscoveryError::MalformedManifest {
51 language: *language,
52 error,
53 })
54 }
55 Err(LibraryMetadataError::MissingManifest(_)) => continue,
56 }
57 }
58 Err(ExtractorDiscoveryError::NotFound)
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 mod extractor_getter {
66 use super::*;
67 use daipendency_extractor::Extractor;
68 use daipendency_extractor_rust::RustExtractor;
69
70 #[test]
71 fn get_extractor_rust() {
72 let extractor = get_extractor(Language::Rust);
73
74 assert_eq!(
75 format!("{:?}", extractor.get_parser_language()),
76 format!("{:?}", RustExtractor::new().get_parser_language())
77 );
78 }
79 }
80
81 mod extractor_discovery {
82 use super::*;
83 use assertables::{assert_matches, assert_ok};
84 use daipendency_extractor::Extractor;
85 use daipendency_extractor_rust::RustExtractor;
86 use std::fs;
87 use tempfile::TempDir;
88
89 #[test]
90 fn no_matching_extractor() {
91 let temp_dir = TempDir::new().unwrap();
92
93 let result = discover_extractor(temp_dir.path());
94
95 assert_matches!(result, Err(ExtractorDiscoveryError::NotFound));
96 }
97
98 #[test]
99 fn malformed_manifest() {
100 let temp_dir = TempDir::new().unwrap();
101 fs::write(temp_dir.path().join("Cargo.toml"), "invalid toml").unwrap();
102
103 let result = discover_extractor(temp_dir.path());
104
105 assert_matches!(
106 result,
107 Err(ExtractorDiscoveryError::MalformedManifest {
108 language: Language::Rust,
109 error: _
110 })
111 );
112 }
113
114 #[test]
115 fn successful_discovery() {
116 let temp_dir = TempDir::new().unwrap();
117 fs::write(
118 temp_dir.path().join("Cargo.toml"),
119 r#"[package]
120name = "test-package"
121version = "0.1.0"
122"#,
123 )
124 .unwrap();
125
126 let result = discover_extractor(temp_dir.path());
127
128 assert_ok!(&result);
129 let discovery = result.unwrap();
130 assert_eq!(discovery.language, Language::Rust);
131 assert_eq!(discovery.library_metadata.name, "test-package");
132 assert_eq!(
133 format!("{:?}", discovery.extractor.get_parser_language()),
134 format!("{:?}", RustExtractor::new().get_parser_language())
135 );
136 }
137 }
138}