const ALIASES: [(&str, &str); 41] = [
("json", "json"),
("jsonc", "jsonc"),
("yaml", "yaml"),
("yml", "yaml"),
("csv", "csv"),
("tsv", "tsv"),
("toml", "toml"),
("ini", "ini"),
("env", "env"),
("dotenv", "env"),
("python", "python"),
("py", "python"),
("rust", "rust"),
("rs", "rust"),
("go", "go"),
("java", "java"),
("kotlin", "kotlin"),
("kt", "kotlin"),
("kts", "kotlin"),
("csharp", "csharp"),
("cs", "csharp"),
("cpp", "cpp"),
("cc", "cpp"),
("cxx", "cpp"),
("hpp", "cpp"),
("hh", "cpp"),
("c", "c"),
("h", "c"),
("javascript", "javascript"),
("js", "javascript"),
("mjs", "javascript"),
("cjs", "javascript"),
("javascriptreact", "javascript"),
("jsx", "javascript"),
("typescript", "typescript"),
("ts", "typescript"),
("typescriptreact", "typescript"),
("tsx", "typescript"),
("sql", "sql"),
("shellscript", "shellscript"),
("sh", "shellscript"),
];
pub(crate) const SUPPORTED_FORMATS: [&str; 20] = [
"json",
"jsonc",
"yaml",
"csv",
"tsv",
"toml",
"ini",
"env",
"python",
"rust",
"go",
"java",
"kotlin",
"csharp",
"cpp",
"c",
"javascript",
"typescript",
"sql",
"shellscript",
];
const SOURCE_FORMATS: [&str; 12] = [
"python",
"rust",
"go",
"java",
"kotlin",
"csharp",
"cpp",
"c",
"javascript",
"typescript",
"sql",
"shellscript",
];
pub(crate) fn is_source(format: &str) -> bool {
SOURCE_FORMATS.contains(&format)
}
pub(crate) const FALLBACK_FORMAT: &str = "unknown";
fn normalise(value: &str) -> String {
super::js::trim(value)
.to_lowercase()
.trim_start_matches('.')
.to_string()
}
pub(crate) fn canonical(format: &str) -> &'static str {
ALIASES
.iter()
.find(|(alias, _)| *alias == format)
.map_or(FALLBACK_FORMAT, |(_, key)| *key)
}
pub(crate) fn resolve_format(format: Option<&str>, filename: Option<&str>) -> &'static str {
if let Some(name) = format {
let direct = canonical(&normalise(name));
if direct != FALLBACK_FORMAT {
return direct;
}
}
let Some(filename) = filename else {
return FALLBACK_FORMAT;
};
let whole = canonical(&normalise(filename));
if whole != FALLBACK_FORMAT {
return whole;
}
if is_dotenv(&super::js::trim(filename).to_lowercase()) {
return canonical("env");
}
filename
.rsplit_once('.')
.map_or(FALLBACK_FORMAT, |(_, extension)| {
canonical(&normalise(extension))
})
}
fn is_dotenv(name: &str) -> bool {
name == ".env"
|| name.starts_with(".env.")
|| name == "env"
|| name
.strip_suffix(".env")
.is_some_and(|stem| !stem.is_empty())
}
#[cfg(test)]
mod dotenv_tests {
use super::resolve_format;
#[test]
fn every_dotenv_spelling_resolves() {
for name in [
".env",
".env.local",
".env.production",
".env.test.local",
"app.env",
"env",
] {
assert_eq!(resolve_format(None, Some(name)), "env", "{name}");
}
}
#[test]
fn a_name_that_merely_starts_with_env_is_not_dotenv() {
for name in [".envrc", "environment.json", "env.ts", "sender.env.rs"] {
assert_ne!(resolve_format(None, Some(name)), "env", "{name}");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_offered_format_resolves_to_itself() {
for format in SUPPORTED_FORMATS {
assert_eq!(resolve_format(Some(format), None), format, "{format}");
}
}
#[test]
fn the_extensions_aliases_are_honoured() {
for (alias, expected) in [
("jsonc", "jsonc"),
("yml", "yaml"),
("tsv", "tsv"),
("dotenv", "env"),
] {
assert_eq!(resolve_format(Some(alias), None), expected, "{alias}");
}
}
#[test]
fn a_name_is_normalised_before_it_is_matched() {
assert_eq!(resolve_format(Some(" JSON "), None), "json");
assert_eq!(resolve_format(Some(".toml"), None), "toml");
}
#[test]
fn a_byte_order_mark_around_a_name_is_whitespace_as_it_is_in_javascript() {
assert_eq!(resolve_format(Some("\u{feff}json"), None), "json");
assert_eq!(resolve_format(Some("json\u{feff}"), None), "json");
assert_eq!(resolve_format(Some("\u{feff}\u{feff}toml"), None), "toml");
assert_eq!(
resolve_format(Some("\u{85}json"), None),
FALLBACK_FORMAT,
"a next-line character is not whitespace in JavaScript"
);
}
#[test]
fn a_filename_supplies_the_format_when_none_is_named() {
assert_eq!(resolve_format(None, Some("config.toml")), "toml");
assert_eq!(resolve_format(None, Some("data.CSV")), "csv");
}
#[test]
fn a_dotfile_resolves_by_its_whole_name() {
assert_eq!(resolve_format(None, Some(".env")), "env");
assert_eq!(resolve_format(None, Some("env")), "env");
}
#[test]
fn anything_unrecognised_falls_back() {
for name in ["markdown", "dockerfile", "", "wat"] {
assert_eq!(resolve_format(Some(name), None), FALLBACK_FORMAT, "{name}");
}
assert_eq!(resolve_format(None, Some("README.md")), FALLBACK_FORMAT);
assert_eq!(resolve_format(None, Some("Makefile")), FALLBACK_FORMAT);
assert_eq!(resolve_format(None, None), FALLBACK_FORMAT);
}
#[test]
fn a_source_language_resolves_to_its_own_extractor() {
for (name, expected) in [
("main.rs", "rust"),
("app.py", "python"),
("main.go", "go"),
("Widget.tsx", "typescript"),
("index.mjs", "javascript"),
("Main.java", "java"),
("query.sql", "sql"),
("deploy.sh", "shellscript"),
("engine.hpp", "cpp"),
("port.h", "c"),
] {
assert_eq!(resolve_format(None, Some(name)), expected, "{name}");
}
assert_eq!(resolve_format(Some("typescriptreact"), None), "typescript");
}
#[test]
fn the_source_list_matches_the_formats_that_are_source_languages() {
for format in SOURCE_FORMATS {
assert!(is_source(format), "{format}");
assert!(
SUPPORTED_FORMATS.contains(&format),
"{format} is a source key but is not offered"
);
}
for format in ["json", "yaml", "csv", "toml", "ini", "env", FALLBACK_FORMAT] {
assert!(!is_source(format), "{format}");
}
}
#[test]
fn an_unresolved_format_defers_to_the_filename() {
assert_eq!(resolve_format(Some("nonsense"), Some("a.toml")), "toml");
}
#[test]
fn the_offered_list_matches_the_alias_table() {
for format in SUPPORTED_FORMATS {
assert!(
ALIASES.iter().any(|(_, key)| *key == format),
"{format} is offered but no alias produces it"
);
}
for (_, key) in ALIASES {
assert!(
SUPPORTED_FORMATS.contains(&key),
"{key} is produced but not offered"
);
}
}
}