1use std::path::Path;
7
8#[derive(Debug, Clone, Copy)]
10pub struct ExtEntry {
11 pub ext: &'static str,
12 pub language_id: &'static str,
14 pub supports_imports: bool,
16 pub canonical: &'static str,
19}
20
21static EXTENSIONS: &[ExtEntry] = &[
24 ExtEntry {
25 ext: "py",
26 language_id: "python",
27 supports_imports: true,
28 canonical: "py",
29 },
30 ExtEntry {
31 ext: "js",
32 language_id: "javascript",
33 supports_imports: true,
34 canonical: "js",
35 },
36 ExtEntry {
37 ext: "mjs",
38 language_id: "javascript",
39 supports_imports: true,
40 canonical: "js",
41 },
42 ExtEntry {
43 ext: "cjs",
44 language_id: "javascript",
45 supports_imports: true,
46 canonical: "js",
47 },
48 ExtEntry {
49 ext: "ts",
50 language_id: "typescript",
51 supports_imports: true,
52 canonical: "ts",
53 },
54 ExtEntry {
55 ext: "tsx",
56 language_id: "typescriptreact",
57 supports_imports: true,
58 canonical: "tsx",
59 },
60 ExtEntry {
61 ext: "jsx",
62 language_id: "javascriptreact",
63 supports_imports: true,
64 canonical: "tsx",
65 },
66 ExtEntry {
67 ext: "go",
68 language_id: "go",
69 supports_imports: true,
70 canonical: "go",
71 },
72 ExtEntry {
73 ext: "java",
74 language_id: "java",
75 supports_imports: true,
76 canonical: "java",
77 },
78 ExtEntry {
79 ext: "kt",
80 language_id: "kotlin",
81 supports_imports: true,
82 canonical: "kt",
83 },
84 ExtEntry {
85 ext: "kts",
86 language_id: "kotlin",
87 supports_imports: true,
88 canonical: "kt",
89 },
90 ExtEntry {
91 ext: "rs",
92 language_id: "rust",
93 supports_imports: true,
94 canonical: "rs",
95 },
96 ExtEntry {
97 ext: "c",
98 language_id: "c",
99 supports_imports: true,
100 canonical: "c",
101 },
102 ExtEntry {
103 ext: "h",
104 language_id: "c",
105 supports_imports: true,
106 canonical: "c",
107 },
108 ExtEntry {
109 ext: "cpp",
110 language_id: "cpp",
111 supports_imports: true,
112 canonical: "cpp",
113 },
114 ExtEntry {
115 ext: "cc",
116 language_id: "cpp",
117 supports_imports: true,
118 canonical: "cpp",
119 },
120 ExtEntry {
121 ext: "cxx",
122 language_id: "cpp",
123 supports_imports: true,
124 canonical: "cpp",
125 },
126 ExtEntry {
127 ext: "hpp",
128 language_id: "cpp",
129 supports_imports: true,
130 canonical: "cpp",
131 },
132 ExtEntry {
133 ext: "hh",
134 language_id: "cpp",
135 supports_imports: true,
136 canonical: "cpp",
137 },
138 ExtEntry {
139 ext: "hxx",
140 language_id: "cpp",
141 supports_imports: true,
142 canonical: "cpp",
143 },
144 ExtEntry {
145 ext: "php",
146 language_id: "php",
147 supports_imports: true,
148 canonical: "php",
149 },
150 ExtEntry {
151 ext: "swift",
152 language_id: "swift",
153 supports_imports: true,
154 canonical: "swift",
155 },
156 ExtEntry {
157 ext: "scala",
158 language_id: "scala",
159 supports_imports: true,
160 canonical: "scala",
161 },
162 ExtEntry {
163 ext: "sc",
164 language_id: "scala",
165 supports_imports: true,
166 canonical: "scala",
167 },
168 ExtEntry {
169 ext: "rb",
170 language_id: "ruby",
171 supports_imports: true,
172 canonical: "rb",
173 },
174 ExtEntry {
175 ext: "cs",
176 language_id: "csharp",
177 supports_imports: true,
178 canonical: "cs",
179 },
180 ExtEntry {
181 ext: "dart",
182 language_id: "dart",
183 supports_imports: true,
184 canonical: "dart",
185 },
186 ExtEntry {
188 ext: "lua",
189 language_id: "lua",
190 supports_imports: false,
191 canonical: "lua",
192 },
193 ExtEntry {
194 ext: "zig",
195 language_id: "zig",
196 supports_imports: false,
197 canonical: "zig",
198 },
199 ExtEntry {
200 ext: "ex",
201 language_id: "elixir",
202 supports_imports: false,
203 canonical: "ex",
204 },
205 ExtEntry {
206 ext: "exs",
207 language_id: "elixir",
208 supports_imports: false,
209 canonical: "ex",
210 },
211 ExtEntry {
212 ext: "hs",
213 language_id: "haskell",
214 supports_imports: false,
215 canonical: "hs",
216 },
217 ExtEntry {
218 ext: "ml",
219 language_id: "ocaml",
220 supports_imports: false,
221 canonical: "ml",
222 },
223 ExtEntry {
224 ext: "mli",
225 language_id: "ocaml",
226 supports_imports: false,
227 canonical: "ml",
228 },
229 ExtEntry {
230 ext: "erl",
231 language_id: "erlang",
232 supports_imports: false,
233 canonical: "erl",
234 },
235 ExtEntry {
236 ext: "hrl",
237 language_id: "erlang",
238 supports_imports: false,
239 canonical: "erl",
240 },
241 ExtEntry {
242 ext: "r",
243 language_id: "r",
244 supports_imports: false,
245 canonical: "r",
246 },
247 ExtEntry {
248 ext: "R",
249 language_id: "r",
250 supports_imports: false,
251 canonical: "r",
252 },
253 ExtEntry {
254 ext: "sh",
255 language_id: "shellscript",
256 supports_imports: false,
257 canonical: "sh",
258 },
259 ExtEntry {
260 ext: "bash",
261 language_id: "shellscript",
262 supports_imports: false,
263 canonical: "sh",
264 },
265 ExtEntry {
266 ext: "jl",
267 language_id: "julia",
268 supports_imports: false,
269 canonical: "jl",
270 },
271 ExtEntry {
273 ext: "css",
274 language_id: "css",
275 supports_imports: true,
276 canonical: "css",
277 },
278 ExtEntry {
279 ext: "html",
280 language_id: "html",
281 supports_imports: false,
282 canonical: "html",
283 },
284 ExtEntry {
285 ext: "htm",
286 language_id: "html",
287 supports_imports: false,
288 canonical: "html",
289 },
290 ExtEntry {
291 ext: "toml",
292 language_id: "toml",
293 supports_imports: false,
294 canonical: "toml",
295 },
296 ExtEntry {
297 ext: "yaml",
298 language_id: "yaml",
299 supports_imports: false,
300 canonical: "yaml",
301 },
302 ExtEntry {
303 ext: "yml",
304 language_id: "yaml",
305 supports_imports: false,
306 canonical: "yaml",
307 },
308 ExtEntry {
309 ext: "clj",
310 language_id: "clojure",
311 supports_imports: false,
312 canonical: "clj",
313 },
314 ExtEntry {
315 ext: "cljs",
316 language_id: "clojurescript",
317 supports_imports: false,
318 canonical: "clj",
319 },
320 ];
323
324pub fn for_extension(ext: &str) -> Option<&'static ExtEntry> {
326 EXTENSIONS.iter().find(|e| e.ext == ext)
327}
328
329pub fn supports_symbols(ext: &str) -> bool {
332 for_extension(ext).is_some()
333}
334
335pub fn supports_imports(ext: &str) -> bool {
337 for_extension(ext).is_some_and(|e| e.supports_imports)
338}
339
340pub fn supports_imports_for_path(path: &Path) -> bool {
342 path.extension()
343 .and_then(|ext| ext.to_str())
344 .is_some_and(|ext| supports_imports(&ext.to_ascii_lowercase()))
345}
346
347pub fn language_id(ext: &str) -> Option<&'static str> {
349 for_extension(ext).map(|e| e.language_id)
350}
351
352pub fn import_extensions() -> impl Iterator<Item = &'static str> {
354 EXTENSIONS
355 .iter()
356 .filter(|e| e.supports_imports)
357 .map(|e| e.ext)
358}
359
360pub fn all_extensions() -> impl Iterator<Item = &'static str> {
362 EXTENSIONS.iter().map(|e| e.ext)
363}
364
365#[cfg(test)]
366mod tests {
367 use super::*;
368
369 #[test]
370 fn all_extensions_unique() {
371 let mut seen = std::collections::HashSet::new();
372 for entry in EXTENSIONS {
373 assert!(seen.insert(entry.ext), "duplicate extension: {}", entry.ext);
374 }
375 }
376
377 #[test]
378 fn kts_supports_imports() {
379 assert!(
380 supports_imports("kts"),
381 "kts should support imports (Kotlin scripts)"
382 );
383 }
384
385 #[test]
386 fn swift_scala_support_imports() {
387 assert!(supports_imports("swift"));
388 assert!(supports_imports("scala"));
389 assert!(supports_imports("sc"));
390 }
391
392 #[test]
393 fn hh_hxx_have_language_id() {
394 assert_eq!(language_id("hh"), Some("cpp"));
395 assert_eq!(language_id("hxx"), Some("cpp"));
396 }
397
398 #[test]
399 fn jsx_tsx_distinct_language_ids() {
400 assert_eq!(language_id("tsx"), Some("typescriptreact"));
401 assert_eq!(language_id("jsx"), Some("javascriptreact"));
402 }
403}