panproto_parse/
registry.rs1use std::path::Path;
4
5use panproto_schema::Schema;
6use rustc_hash::FxHashMap;
7
8use crate::error::ParseError;
9use crate::theory_extract::ExtractedTheoryMeta;
10
11pub trait AstParser: Send + Sync {
16 fn protocol_name(&self) -> &str;
18
19 fn parse(&self, source: &[u8], file_path: &str) -> Result<Schema, ParseError>;
25
26 fn emit(&self, schema: &Schema) -> Result<Vec<u8>, ParseError>;
35
36 fn supported_extensions(&self) -> &[&str];
38
39 fn theory_meta(&self) -> &ExtractedTheoryMeta;
41}
42
43pub struct ParserRegistry {
48 parsers: FxHashMap<String, Box<dyn AstParser>>,
50 extension_map: FxHashMap<String, String>,
52}
53
54impl ParserRegistry {
55 #[must_use]
57 pub fn new() -> Self {
58 let mut registry = Self {
59 parsers: FxHashMap::default(),
60 extension_map: FxHashMap::default(),
61 };
62
63 registry.register(Box::new(
65 crate::languages::typescript::TypeScriptParser::new(),
66 ));
67 registry.register(Box::new(crate::languages::typescript::TsxParser::new()));
68 registry.register(Box::new(crate::languages::python::PythonParser::new()));
69 registry.register(Box::new(crate::languages::rust_lang::RustParser::new()));
70 registry.register(Box::new(crate::languages::java::JavaParser::new()));
71 registry.register(Box::new(crate::languages::go_lang::GoParser::new()));
72 registry.register(Box::new(crate::languages::swift::SwiftParser::new()));
73 registry.register(Box::new(crate::languages::kotlin::KotlinParser::new()));
74 registry.register(Box::new(crate::languages::csharp::CSharpParser::new()));
75 registry.register(Box::new(crate::languages::c_lang::CParser::new()));
76 registry.register(Box::new(crate::languages::cpp::CppParser::new()));
77
78 registry
79 }
80
81 pub fn register(&mut self, parser: Box<dyn AstParser>) {
83 let name = parser.protocol_name().to_owned();
84 for ext in parser.supported_extensions() {
85 self.extension_map.insert((*ext).to_owned(), name.clone());
86 }
87 self.parsers.insert(name, parser);
88 }
89
90 #[must_use]
95 pub fn detect_language(&self, path: &Path) -> Option<&str> {
96 path.extension()
97 .and_then(|ext| ext.to_str())
98 .and_then(|ext| self.extension_map.get(ext))
99 .map(String::as_str)
100 }
101
102 pub fn parse_file(&self, path: &Path, content: &[u8]) -> Result<Schema, ParseError> {
109 let protocol = self
110 .detect_language(path)
111 .ok_or_else(|| ParseError::UnknownLanguage {
112 extension: path
113 .extension()
114 .and_then(|e| e.to_str())
115 .unwrap_or("")
116 .to_owned(),
117 })?;
118
119 self.parse_with_protocol(protocol, content, &path.display().to_string())
120 }
121
122 pub fn parse_with_protocol(
128 &self,
129 protocol: &str,
130 content: &[u8],
131 file_path: &str,
132 ) -> Result<Schema, ParseError> {
133 let parser = self
134 .parsers
135 .get(protocol)
136 .ok_or_else(|| ParseError::UnknownLanguage {
137 extension: protocol.to_owned(),
138 })?;
139
140 parser.parse(content, file_path)
141 }
142
143 pub fn emit_with_protocol(
149 &self,
150 protocol: &str,
151 schema: &Schema,
152 ) -> Result<Vec<u8>, ParseError> {
153 let parser = self
154 .parsers
155 .get(protocol)
156 .ok_or_else(|| ParseError::UnknownLanguage {
157 extension: protocol.to_owned(),
158 })?;
159
160 parser.emit(schema)
161 }
162
163 #[must_use]
165 pub fn theory_meta(&self, protocol: &str) -> Option<&ExtractedTheoryMeta> {
166 self.parsers.get(protocol).map(|p| p.theory_meta())
167 }
168
169 pub fn protocol_names(&self) -> impl Iterator<Item = &str> {
171 self.parsers.keys().map(String::as_str)
172 }
173
174 #[must_use]
176 pub fn len(&self) -> usize {
177 self.parsers.len()
178 }
179
180 #[must_use]
182 pub fn is_empty(&self) -> bool {
183 self.parsers.is_empty()
184 }
185}
186
187impl Default for ParserRegistry {
188 fn default() -> Self {
189 Self::new()
190 }
191}