const EXTENSION_MAP: &[(&str, &str)] = &[
("c", "c"),
("cc", "cpp"),
("cjs", "javascript"),
("cpp", "cpp"),
("cxx", "cpp"),
("f", "fortran"),
("f03", "fortran"),
("f08", "fortran"),
("f77", "fortran"),
("f90", "fortran"),
("f95", "fortran"),
("for", "fortran"),
("ftn", "fortran"),
("h", "cpp"),
("cs", "csharp"),
("hpp", "cpp"),
("hxx", "cpp"),
("js", "javascript"),
("mjs", "javascript"),
("go", "go"),
("java", "java"),
("kt", "kotlin"),
("kts", "kotlin"),
("py", "python"),
("rs", "rust"),
("ts", "typescript"),
("tsx", "tsx"),
("html", "html"),
("htm", "html"),
("md", "markdown"),
("mdx", "markdown"),
("astro", "astro"),
("css", "css"),
("yaml", "yaml"),
("yml", "yaml"),
("json", "json"),
("toml", "toml"),
];
#[must_use]
pub fn language_for_extension(ext: &str) -> Option<&'static str> {
EXTENSION_MAP
.iter()
.find(|(e, _)| e.eq_ignore_ascii_case(ext))
.map(|(_, lang)| *lang)
}
#[must_use]
pub fn supported_extensions() -> Vec<&'static str> {
EXTENSION_MAP.iter().map(|(ext, _)| *ext).collect()
}
#[must_use]
pub fn supported_languages() -> &'static [&'static str] {
&[
"rust",
"go",
"java",
"kotlin",
"python",
"typescript",
"tsx",
"javascript",
"fortran",
"c",
"cpp",
"csharp",
"html",
"markdown",
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_language_for_extension_happy_path() {
assert_eq!(language_for_extension("rs"), Some("rust"));
assert_eq!(language_for_extension("py"), Some("python"));
assert_eq!(language_for_extension("go"), Some("go"));
assert_eq!(language_for_extension("java"), Some("java"));
assert_eq!(language_for_extension("ts"), Some("typescript"));
assert_eq!(language_for_extension("tsx"), Some("tsx"));
assert_eq!(language_for_extension("f90"), Some("fortran"));
assert_eq!(language_for_extension("for"), Some("fortran"));
assert_eq!(language_for_extension("ftn"), Some("fortran"));
assert_eq!(language_for_extension("c"), Some("c"));
assert_eq!(language_for_extension("cpp"), Some("cpp"));
assert_eq!(language_for_extension("h"), Some("cpp"));
assert_eq!(language_for_extension("hpp"), Some("cpp"));
assert_eq!(language_for_extension("cc"), Some("cpp"));
assert_eq!(language_for_extension("kt"), Some("kotlin"));
assert_eq!(language_for_extension("kts"), Some("kotlin"));
}
#[test]
fn test_supported_file_ext_pattern_covers_all_extension_map_entries() {
#[cfg(feature = "schemars")]
for (ext, _lang) in EXTENSION_MAP {
let in_alternation = crate::schema_helpers::SUPPORTED_FILE_EXT_PATTERN
.contains(&format!("{ext}|"))
|| crate::schema_helpers::SUPPORTED_FILE_EXT_PATTERN.contains(&format!("{ext})"));
assert!(
in_alternation,
"SUPPORTED_FILE_EXT_PATTERN is missing extension '{ext}' from EXTENSION_MAP; \
add it to schema_helpers.rs"
);
}
}
#[test]
fn test_language_for_extension_edge_case() {
assert_eq!(language_for_extension("unknown"), None);
assert_eq!(language_for_extension(""), None);
assert_eq!(language_for_extension("RS"), Some("rust"));
assert_eq!(language_for_extension("F90"), Some("fortran"));
assert_eq!(language_for_extension("FOR"), Some("fortran"));
}
}