Skip to main content

dk_engine/parser/
registry.rs

1use super::engine::QueryDrivenParser;
2use super::lang_config::LanguageConfig;
3use super::langs;
4use super::LanguageParser;
5use dk_core::{FileAnalysis, Result};
6use std::collections::HashMap;
7use std::path::Path;
8use std::sync::Arc;
9
10/// Central registry that maps file extensions to their language parsers.
11pub struct ParserRegistry {
12    parsers: HashMap<String, Arc<dyn LanguageParser>>,
13}
14
15impl ParserRegistry {
16    /// Create a new registry with all built-in language parsers registered.
17    pub fn new() -> Self {
18        let mut parsers: HashMap<String, Arc<dyn LanguageParser>> = HashMap::new();
19
20        let mut register = |config: Box<dyn LanguageConfig>| {
21            let exts: Vec<String> = config.extensions().iter().map(|s| s.to_string()).collect();
22            match QueryDrivenParser::new(config) {
23                Ok(parser) => {
24                    let arc: Arc<dyn LanguageParser> = Arc::new(parser);
25                    for ext in exts {
26                        parsers.insert(ext, Arc::clone(&arc));
27                    }
28                }
29                Err(e) => {
30                    tracing::error!("Failed to initialize parser: {e}");
31                }
32            }
33        };
34
35        register(Box::new(langs::rust::RustConfig));
36        register(Box::new(langs::python::PythonConfig));
37        register(Box::new(langs::go::GoConfig));
38        register(Box::new(langs::java::JavaConfig));
39        register(Box::new(langs::cpp::CppConfig));
40        register(Box::new(langs::csharp::CSharpConfig));
41        register(Box::new(langs::ruby::RubyConfig));
42        register(Box::new(langs::php::PhpConfig));
43        register(Box::new(langs::swift::SwiftConfig));
44
45        // TypeScript uses a wrapper (TypeScriptParser) for dedup logic
46        let ts_parser = langs::typescript::TypeScriptParser::new();
47        match ts_parser {
48            Ok(parser) => {
49                let arc: Arc<dyn LanguageParser> = Arc::new(parser);
50                for ext in ["ts", "tsx", "js", "jsx"] {
51                    parsers.insert(ext.to_string(), Arc::clone(&arc));
52                }
53            }
54            Err(e) => {
55                tracing::error!("Failed to initialize TypeScript parser: {e}");
56            }
57        }
58
59        Self { parsers }
60    }
61
62    /// Return `true` if the file extension is handled by a registered parser.
63    pub fn supports_file(&self, path: &Path) -> bool {
64        path.extension()
65            .and_then(|e| e.to_str())
66            .map(|ext| self.parsers.contains_key(ext))
67            .unwrap_or(false)
68    }
69
70    /// Parse a source file, selecting the parser by file extension.
71    pub fn parse_file(&self, path: &Path, source: &[u8]) -> Result<FileAnalysis> {
72        let ext = path
73            .extension()
74            .and_then(|e| e.to_str())
75            .ok_or_else(|| dk_core::Error::UnsupportedLanguage("no extension".into()))?;
76
77        let parser = self
78            .parsers
79            .get(ext)
80            .ok_or_else(|| dk_core::Error::UnsupportedLanguage(ext.into()))?;
81
82        parser.parse_file(source, path)
83    }
84}
85
86impl Default for ParserRegistry {
87    fn default() -> Self {
88        Self::new()
89    }
90}