use std::collections::HashMap;
use std::path::Path;
use std::sync::LazyLock;
static LANGUAGE_EXTENSIONS: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(|| {
let mut m = HashMap::new();
m.insert(".rs", "rust");
m.insert(".ts", "typescript");
m.insert(".tsx", "typescriptreact");
m.insert(".js", "javascript");
m.insert(".jsx", "javascriptreact");
m.insert(".mjs", "javascript");
m.insert(".cjs", "javascript");
m.insert(".mts", "typescript");
m.insert(".cts", "typescript");
m.insert(".py", "python");
m.insert(".pyi", "python");
m.insert(".pyw", "python");
m.insert(".go", "go");
m.insert(".mod", "go.mod");
m.insert(".sum", "go.sum");
m.insert(".java", "java");
m.insert(".c", "c");
m.insert(".h", "c");
m.insert(".cpp", "cpp");
m.insert(".cxx", "cpp");
m.insert(".cc", "cpp");
m.insert(".hpp", "cpp");
m.insert(".hxx", "cpp");
m.insert(".hh", "cpp");
m.insert(".cs", "csharp");
m.insert(".rb", "ruby");
m.insert(".rake", "ruby");
m.insert(".gemspec", "ruby");
m.insert(".php", "php");
m.insert(".swift", "swift");
m.insert(".kt", "kotlin");
m.insert(".kts", "kotlin");
m.insert(".scala", "scala");
m.insert(".sc", "scala");
m.insert(".ex", "elixir");
m.insert(".exs", "elixir");
m.insert(".erl", "erlang");
m.insert(".hrl", "erlang");
m.insert(".hs", "haskell");
m.insert(".lhs", "haskell");
m.insert(".lua", "lua");
m.insert(".dart", "dart");
m.insert(".vue", "vue");
m.insert(".svelte", "svelte");
m.insert(".html", "html");
m.insert(".htm", "html");
m.insert(".css", "css");
m.insert(".scss", "scss");
m.insert(".sass", "sass");
m.insert(".less", "less");
m.insert(".json", "json");
m.insert(".jsonc", "jsonc");
m.insert(".yaml", "yaml");
m.insert(".yml", "yaml");
m.insert(".toml", "toml");
m.insert(".md", "markdown");
m.insert(".mdx", "mdx");
m.insert(".sh", "shellscript");
m.insert(".bash", "shellscript");
m.insert(".zsh", "shellscript");
m.insert(".fish", "fish");
m.insert(".sql", "sql");
m.insert(".zig", "zig");
m.insert(".nim", "nim");
m.insert(".ml", "ocaml");
m.insert(".mli", "ocaml");
m.insert(".fs", "fsharp");
m.insert(".fsi", "fsharp");
m.insert(".fsx", "fsharp");
m.insert(".clj", "clojure");
m.insert(".cljs", "clojure");
m.insert(".cljc", "clojure");
m.insert(".edn", "clojure");
m.insert(".r", "r");
m.insert(".R", "r");
m.insert(".jl", "julia");
m.insert(".tf", "terraform");
m.insert(".tfvars", "terraform");
m.insert(".dockerfile", "dockerfile");
m.insert(".graphql", "graphql");
m.insert(".gql", "graphql");
m.insert(".proto", "proto");
m.insert(".xml", "xml");
m.insert(".xsl", "xml");
m.insert(".xsd", "xml");
m
});
pub fn language_id_for_path(path: &Path) -> &'static str {
path.extension()
.and_then(|ext| ext.to_str())
.map(|ext| format!(".{}", ext))
.and_then(|ext| LANGUAGE_EXTENSIONS.get(ext.as_str()).copied())
.unwrap_or("plaintext")
}
pub fn language_id_for_extension(ext: &str) -> &'static str {
LANGUAGE_EXTENSIONS.get(ext).copied().unwrap_or("plaintext")
}
pub fn is_supported_extension(ext: &str) -> bool {
LANGUAGE_EXTENSIONS.contains_key(ext)
}
pub fn supported_extensions() -> impl Iterator<Item = &'static str> {
LANGUAGE_EXTENSIONS.keys().copied()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rust_extension() {
assert_eq!(language_id_for_extension(".rs"), "rust");
}
#[test]
fn test_typescript_extension() {
assert_eq!(language_id_for_extension(".ts"), "typescript");
assert_eq!(language_id_for_extension(".tsx"), "typescriptreact");
}
#[test]
fn test_python_extension() {
assert_eq!(language_id_for_extension(".py"), "python");
}
#[test]
fn test_unknown_extension() {
assert_eq!(language_id_for_extension(".xyz"), "plaintext");
}
#[test]
fn test_language_id_for_path() {
let path = Path::new("/some/path/file.rs");
assert_eq!(language_id_for_path(path), "rust");
let path = Path::new("main.py");
assert_eq!(language_id_for_path(path), "python");
}
}