use std::collections::HashMap;
use std::sync::LazyLock;
static LANGUAGE_MAP: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(|| {
let mut m = HashMap::new();
m.insert("py", "python");
m.insert("python3", "python");
m.insert("py3", "python");
m.insert("js", "javascript");
m.insert("jsx", "javascript");
m.insert("ts", "typescript");
m.insert("tsx", "typescript");
m.insert("rb", "ruby");
m.insert("rs", "rust");
m.insert("sh", "shell");
m.insert("bash", "shell");
m.insert("zsh", "shell");
m.insert("fish", "shell");
m.insert("ksh", "shell");
m.insert("csh", "shell");
m.insert("ps1", "powershell");
m.insert("psm1", "powershell");
m.insert("cs", "csharp");
m.insert("c#", "csharp");
m.insert("fs", "fsharp");
m.insert("f#", "fsharp");
m.insert("vb", "vbnet");
m.insert("vbnet", "vbnet");
m.insert("vb.net", "vbnet");
m.insert("objc", "objectivec");
m.insert("objective-c", "objectivec");
m.insert("objectivec", "objectivec");
m.insert("mm", "objectivecpp");
m.insert("kt", "kotlin");
m.insert("kts", "kotlin");
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("ml", "ocaml");
m.insert("mli", "ocaml");
m.insert("clj", "clojure");
m.insert("cljs", "clojurescript");
m.insert("cljc", "clojure");
m.insert("coffee", "coffeescript");
m.insert("litcoffee", "coffeescript");
m.insert("pl", "perl");
m.insert("pm", "perl");
m.insert("php3", "php");
m.insert("php4", "php");
m.insert("php5", "php");
m.insert("php7", "php");
m.insert("php8", "php");
m.insert("phtml", "php");
m.insert("lua", "lua");
m.insert("r", "r");
m.insert("rscript", "r");
m.insert("jl", "julia");
m.insert("nim", "nim");
m.insert("cr", "crystal");
m.insert("d", "d");
m.insert("dart", "dart");
m.insert("elm", "elm");
m.insert("groovy", "groovy");
m.insert("gvy", "groovy");
m.insert("gradle", "groovy");
m.insert("m", "matlab");
m.insert("mat", "matlab");
m.insert("pas", "pascal");
m.insert("pp", "pascal");
m.insert("delphi", "pascal");
m.insert("f90", "fortran");
m.insert("f95", "fortran");
m.insert("f03", "fortran");
m.insert("f08", "fortran");
m.insert("for", "fortran");
m.insert("f", "fortran");
m.insert("cob", "cobol");
m.insert("cbl", "cobol");
m.insert("asm", "assembly");
m.insert("nasm", "assembly");
m.insert("masm", "assembly");
m.insert("s", "assembly");
m.insert("v", "verilog");
m.insert("sv", "systemverilog");
m.insert("vhd", "vhdl");
m.insert("vhdl", "vhdl");
m.insert("htm", "html");
m.insert("xhtml", "html");
m.insert("xml", "xml");
m.insert("xsl", "xml");
m.insert("xslt", "xml");
m.insert("svg", "xml");
m.insert("rss", "xml");
m.insert("atom", "xml");
m.insert("md", "markdown");
m.insert("mdx", "markdown");
m.insert("mkd", "markdown");
m.insert("rst", "restructuredtext");
m.insert("rest", "restructuredtext");
m.insert("tex", "latex");
m.insert("ltx", "latex");
m.insert("sty", "latex");
m.insert("json", "json");
m.insert("json5", "json");
m.insert("jsonc", "json");
m.insert("geojson", "json");
m.insert("yml", "yaml");
m.insert("yaml", "yaml");
m.insert("toml", "toml");
m.insert("ini", "ini");
m.insert("cfg", "ini");
m.insert("conf", "ini");
m.insert("csv", "csv");
m.insert("tsv", "csv");
m.insert("graphql", "graphql");
m.insert("gql", "graphql");
m.insert("proto", "protobuf");
m.insert("protobuf", "protobuf");
m.insert("thrift", "thrift");
m.insert("avro", "avro");
m.insert("css", "css");
m.insert("scss", "scss");
m.insert("sass", "sass");
m.insert("less", "less");
m.insert("styl", "stylus");
m.insert("stylus", "stylus");
m.insert("dockerfile", "dockerfile");
m.insert("docker", "dockerfile");
m.insert("tf", "terraform");
m.insert("hcl", "terraform");
m.insert("nix", "nix");
m.insert("cmake", "cmake");
m.insert("makefile", "makefile");
m.insert("make", "makefile");
m.insert("mk", "makefile");
m.insert("bat", "batch");
m.insert("cmd", "batch");
m.insert("ps", "powershell");
m.insert("pwsh", "powershell");
m.insert("awk", "awk");
m.insert("sed", "sed");
m.insert("sql", "sql");
m.insert("mysql", "sql");
m.insert("pgsql", "sql");
m.insert("plsql", "sql");
m.insert("tsql", "sql");
m.insert("sqlite", "sql");
m.insert("psql", "sql");
m.insert("lisp", "lisp");
m.insert("el", "lisp");
m.insert("rkt", "racket");
m.insert("scm", "scheme");
m.insert("ss", "scheme");
m.insert("sml", "sml");
m.insert("sig", "sml");
m.insert("pro", "prolog");
m.insert("P", "prolog");
m.insert("c", "c");
m.insert("h", "c");
m.insert("cpp", "cpp");
m.insert("cc", "cpp");
m.insert("cxx", "cpp");
m.insert("hpp", "cpp");
m.insert("hxx", "cpp");
m.insert("c++", "cpp");
m.insert("zig", "zig");
m.insert("ada", "ada");
m.insert("adb", "ada");
m.insert("ads", "ada");
m.insert("vue", "vue");
m.insert("svelte", "svelte");
m.insert("hbs", "handlebars");
m.insert("handlebars", "handlebars");
m.insert("mustache", "mustache");
m.insert("ejs", "ejs");
m.insert("pug", "pug");
m.insert("jade", "pug");
m.insert("erb", "erb");
m.insert("haml", "haml");
m.insert("slim", "slim");
m.insert("twig", "twig");
m.insert("jinja", "jinja2");
m.insert("jinja2", "jinja2");
m.insert("j2", "jinja2");
m.insert("liquid", "liquid");
m.insert("swift", "swift");
m.insert("java", "java");
m.insert("go", "go");
m.insert("golang", "go");
m.insert("wasm", "wasm");
m.insert("wat", "wasm");
m.insert("sol", "solidity");
m.insert("vy", "vyper");
m.insert("move", "move");
m.insert("cairo", "cairo");
m.insert("diff", "diff");
m.insert("patch", "diff");
m.insert("applescript", "applescript");
m.insert("osascript", "applescript");
m.insert("ahk", "autohotkey");
m.insert("autohotkey", "autohotkey");
m
});
static KNOWN_LANGUAGES: LazyLock<Vec<&'static str>> = LazyLock::new(|| {
vec![
"python",
"javascript",
"typescript",
"ruby",
"rust",
"go",
"java",
"kotlin",
"scala",
"swift",
"dart",
"elixir",
"erlang",
"haskell",
"ocaml",
"clojure",
"perl",
"php",
"lua",
"r",
"julia",
"nim",
"crystal",
"shell",
"bash",
"powershell",
"csharp",
"fsharp",
"vbnet",
"objectivec",
"cpp",
"c",
"zig",
"ada",
"fortran",
"cobol",
"pascal",
"assembly",
"verilog",
"vhdl",
"systemverilog",
"sql",
"html",
"css",
"scss",
"sass",
"less",
"xml",
"json",
"yaml",
"toml",
"markdown",
"latex",
"graphql",
"protobuf",
"dockerfile",
"terraform",
"nix",
"makefile",
"cmake",
"batch",
"vue",
"svelte",
"handlebars",
"mustache",
"ejs",
"pug",
"erb",
"haml",
"slim",
"twig",
"jinja2",
"liquid",
"diff",
"wasm",
"solidity",
"matlab",
"groovy",
"coffeescript",
"lisp",
"scheme",
"racket",
"prolog",
"sml",
"ini",
"csv",
"restructuredtext",
"applescript",
"autohotkey",
]
});
pub fn normalize_language(lang: &str) -> String {
let lower = lang.trim().to_lowercase();
if let Some(&canonical) = LANGUAGE_MAP.get(lower.as_str()) {
return canonical.to_string();
}
if KNOWN_LANGUAGES.contains(&lower.as_str()) {
return lower;
}
lower
}
pub fn is_known_language(name: &str) -> bool {
let lower = name.trim().to_lowercase();
LANGUAGE_MAP.contains_key(lower.as_str()) || KNOWN_LANGUAGES.contains(&lower.as_str())
}