daipendency/
extractors.rs

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