1use std::collections::HashMap;
7use std::path::Path;
8use std::sync::LazyLock;
9
10static LANGUAGE_EXTENSIONS: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(|| {
12 let mut m = HashMap::new();
13
14 m.insert(".rs", "rust");
16
17 m.insert(".ts", "typescript");
19 m.insert(".tsx", "typescriptreact");
20 m.insert(".js", "javascript");
21 m.insert(".jsx", "javascriptreact");
22 m.insert(".mjs", "javascript");
23 m.insert(".cjs", "javascript");
24 m.insert(".mts", "typescript");
25 m.insert(".cts", "typescript");
26
27 m.insert(".py", "python");
29 m.insert(".pyi", "python");
30 m.insert(".pyw", "python");
31
32 m.insert(".go", "go");
34 m.insert(".mod", "go.mod");
35 m.insert(".sum", "go.sum");
36
37 m.insert(".java", "java");
39
40 m.insert(".c", "c");
42 m.insert(".h", "c");
43 m.insert(".cpp", "cpp");
44 m.insert(".cxx", "cpp");
45 m.insert(".cc", "cpp");
46 m.insert(".hpp", "cpp");
47 m.insert(".hxx", "cpp");
48 m.insert(".hh", "cpp");
49
50 m.insert(".cs", "csharp");
52
53 m.insert(".rb", "ruby");
55 m.insert(".rake", "ruby");
56 m.insert(".gemspec", "ruby");
57
58 m.insert(".php", "php");
60
61 m.insert(".swift", "swift");
63
64 m.insert(".kt", "kotlin");
66 m.insert(".kts", "kotlin");
67
68 m.insert(".scala", "scala");
70 m.insert(".sc", "scala");
71
72 m.insert(".ex", "elixir");
74 m.insert(".exs", "elixir");
75
76 m.insert(".erl", "erlang");
78 m.insert(".hrl", "erlang");
79
80 m.insert(".hs", "haskell");
82 m.insert(".lhs", "haskell");
83
84 m.insert(".lua", "lua");
86
87 m.insert(".dart", "dart");
89
90 m.insert(".vue", "vue");
92
93 m.insert(".svelte", "svelte");
95
96 m.insert(".html", "html");
98 m.insert(".htm", "html");
99 m.insert(".css", "css");
100 m.insert(".scss", "scss");
101 m.insert(".sass", "sass");
102 m.insert(".less", "less");
103
104 m.insert(".json", "json");
106 m.insert(".jsonc", "jsonc");
107 m.insert(".yaml", "yaml");
108 m.insert(".yml", "yaml");
109 m.insert(".toml", "toml");
110
111 m.insert(".md", "markdown");
113 m.insert(".mdx", "mdx");
114
115 m.insert(".sh", "shellscript");
117 m.insert(".bash", "shellscript");
118 m.insert(".zsh", "shellscript");
119 m.insert(".fish", "fish");
120
121 m.insert(".sql", "sql");
123
124 m.insert(".zig", "zig");
126
127 m.insert(".nim", "nim");
129
130 m.insert(".ml", "ocaml");
132 m.insert(".mli", "ocaml");
133
134 m.insert(".fs", "fsharp");
136 m.insert(".fsi", "fsharp");
137 m.insert(".fsx", "fsharp");
138
139 m.insert(".clj", "clojure");
141 m.insert(".cljs", "clojure");
142 m.insert(".cljc", "clojure");
143 m.insert(".edn", "clojure");
144
145 m.insert(".r", "r");
147 m.insert(".R", "r");
148
149 m.insert(".jl", "julia");
151
152 m.insert(".tf", "terraform");
154 m.insert(".tfvars", "terraform");
155
156 m.insert(".dockerfile", "dockerfile");
158
159 m.insert(".graphql", "graphql");
161 m.insert(".gql", "graphql");
162
163 m.insert(".proto", "proto");
165
166 m.insert(".xml", "xml");
168 m.insert(".xsl", "xml");
169 m.insert(".xsd", "xml");
170
171 m
172});
173
174pub fn language_id_for_path(path: &Path) -> &'static str {
176 path.extension()
177 .and_then(|ext| ext.to_str())
178 .map(|ext| format!(".{}", ext))
179 .and_then(|ext| LANGUAGE_EXTENSIONS.get(ext.as_str()).copied())
180 .unwrap_or("plaintext")
181}
182
183pub fn language_id_for_extension(ext: &str) -> &'static str {
185 LANGUAGE_EXTENSIONS.get(ext).copied().unwrap_or("plaintext")
186}
187
188pub fn is_supported_extension(ext: &str) -> bool {
190 LANGUAGE_EXTENSIONS.contains_key(ext)
191}
192
193pub fn supported_extensions() -> impl Iterator<Item = &'static str> {
195 LANGUAGE_EXTENSIONS.keys().copied()
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_rust_extension() {
204 assert_eq!(language_id_for_extension(".rs"), "rust");
205 }
206
207 #[test]
208 fn test_typescript_extension() {
209 assert_eq!(language_id_for_extension(".ts"), "typescript");
210 assert_eq!(language_id_for_extension(".tsx"), "typescriptreact");
211 }
212
213 #[test]
214 fn test_python_extension() {
215 assert_eq!(language_id_for_extension(".py"), "python");
216 }
217
218 #[test]
219 fn test_unknown_extension() {
220 assert_eq!(language_id_for_extension(".xyz"), "plaintext");
221 }
222
223 #[test]
224 fn test_language_id_for_path() {
225 let path = Path::new("/some/path/file.rs");
226 assert_eq!(language_id_for_path(path), "rust");
227
228 let path = Path::new("main.py");
229 assert_eq!(language_id_for_path(path), "python");
230 }
231}