sem_core/parser/
registry.rs1use std::collections::HashMap;
2use std::path::Path;
3
4use super::plugin::SemanticParserPlugin;
5
6pub struct ParserRegistry {
7 plugins: Vec<Box<dyn SemanticParserPlugin>>,
8 extension_map: HashMap<String, usize>, }
10
11impl ParserRegistry {
12 pub fn new() -> Self {
13 Self {
14 plugins: Vec::new(),
15 extension_map: HashMap::new(),
16 }
17 }
18
19 pub fn register(&mut self, plugin: Box<dyn SemanticParserPlugin>) {
20 let idx = self.plugins.len();
21 for ext in plugin.extensions() {
22 self.extension_map.insert(ext.to_string(), idx);
23 }
24 self.plugins.push(plugin);
25 }
26
27 pub fn get_plugin(&self, file_path: &str) -> Option<&dyn SemanticParserPlugin> {
28 for ext in get_extensions(file_path) {
29 if let Some(&idx) = self.extension_map.get(&ext) {
30 return Some(self.plugins[idx].as_ref());
31 }
32 }
33 self.get_plugin_by_id("fallback")
35 }
36
37 pub fn get_plugin_by_id(&self, id: &str) -> Option<&dyn SemanticParserPlugin> {
38 self.plugins
39 .iter()
40 .find(|p| p.id() == id)
41 .map(|p| p.as_ref())
42 }
43}
44
45fn get_extensions(file_path: &str) -> Vec<String> {
46 let Some(file_name) = Path::new(file_path)
47 .file_name()
48 .and_then(|name| name.to_str())
49 else {
50 return Vec::new();
51 };
52
53 let file_name = file_name.to_lowercase();
54 let mut extensions = Vec::new();
55
56 for (idx, ch) in file_name.char_indices() {
57 if ch == '.' {
58 extensions.push(file_name[idx..].to_string());
59 }
60 }
61
62 extensions
63}
64
65#[cfg(test)]
66mod tests {
67 use crate::parser::plugins::create_default_registry;
68
69 #[test]
70 fn test_registry_matches_compound_svelte_typescript_suffix() {
71 let registry = create_default_registry();
72 let plugin = registry
73 .get_plugin("src/routes/+page.svelte.ts")
74 .expect("plugin should exist");
75
76 assert_eq!(plugin.id(), "svelte");
77 }
78
79 #[test]
80 fn test_registry_matches_compound_svelte_javascript_suffix() {
81 let registry = create_default_registry();
82 let plugin = registry
83 .get_plugin("src/routes/+layout.svelte.js")
84 .expect("plugin should exist");
85
86 assert_eq!(plugin.id(), "svelte");
87 }
88
89 #[test]
90 fn test_registry_matches_svelte_test_suffix() {
91 let registry = create_default_registry();
92 let plugin = registry
93 .get_plugin("src/lib/multiplier.svelte.test.js")
94 .expect("plugin should exist");
95
96 assert_eq!(plugin.id(), "svelte");
97 }
98
99 #[test]
100 fn test_registry_prefers_svelte_plugin_for_component_files() {
101 let registry = create_default_registry();
102 let plugin = registry
103 .get_plugin("src/lib/Component.svelte")
104 .expect("plugin should exist");
105
106 assert_eq!(plugin.id(), "svelte");
107 }
108
109 #[test]
110 fn test_registry_matches_typescript_module_suffix() {
111 let registry = create_default_registry();
112 let plugin = registry
113 .get_plugin("src/lib/index.mts")
114 .expect("plugin should exist");
115
116 assert_eq!(plugin.id(), "code");
117 }
118
119 #[test]
120 fn test_registry_matches_typescript_commonjs_suffix() {
121 let registry = create_default_registry();
122 let plugin = registry
123 .get_plugin("src/lib/index.cts")
124 .expect("plugin should exist");
125
126 assert_eq!(plugin.id(), "code");
127 }
128}