1pub mod normalizer;
8pub mod parser;
9
10use std::path::Path;
11
12use dupes_core::analyzer::LanguageAnalyzer;
13use dupes_core::code_unit::CodeUnit;
14use dupes_core::config::AnalysisConfig;
15
16pub struct RustAnalyzer;
18
19impl RustAnalyzer {
20 #[must_use]
21 pub const fn new() -> Self {
22 Self
23 }
24}
25
26impl Default for RustAnalyzer {
27 fn default() -> Self {
28 Self::new()
29 }
30}
31
32impl LanguageAnalyzer for RustAnalyzer {
33 fn file_extensions(&self) -> &[&str] {
34 &["rs"]
35 }
36
37 fn parse_file(
38 &self,
39 path: &Path,
40 source: &str,
41 config: &AnalysisConfig,
42 ) -> Result<Vec<CodeUnit>, Box<dyn std::error::Error + Send + Sync>> {
43 parser::parse_source(path, source, config.min_nodes, config.min_lines)
44 .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { e.into() })
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51 use std::path::PathBuf;
52
53 #[test]
54 fn rust_analyzer_through_trait() {
55 let analyzer = RustAnalyzer::new();
56 let config = AnalysisConfig {
57 min_nodes: 1,
58 min_lines: 0,
59 };
60 let source = r#"
61 fn foo(x: i32) -> i32 {
62 let y = x + 1;
63 y * 2
64 }
65 #[test]
66 fn test_foo() {
67 let z = 1;
68 let w = z + 1;
69 assert_eq!(w, 2);
70 }
71 "#;
72 let path = PathBuf::from("test.rs");
73 let units = analyzer.parse_file(&path, source, &config).unwrap();
74
75 assert!(units.len() >= 2);
77
78 let prod: Vec<_> = units.iter().filter(|u| u.name == "foo").collect();
80 assert_eq!(prod.len(), 1);
81 assert!(!prod[0].is_test);
82
83 let test: Vec<_> = units.iter().filter(|u| u.name == "test_foo").collect();
85 assert_eq!(test.len(), 1);
86 assert!(test[0].is_test);
87
88 assert!(!analyzer.is_test_code(prod[0]));
90 }
91}