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"),
("shellscript", "shellscript"),
("sh", "shellscript"),
("bash", "shellscript"),
("zsh", "shellscript"),
("php", "php"),
("ruby", "ruby"),
("rb", "ruby"),
("perl", "perl"),
("pl", "perl"),
("pm", "perl"),
("csharp", "csharp"),
("cs", "csharp"),
("javascript", "javascript"),
("javascriptreact", "javascript"),
("js", "javascript"),
("jsx", "javascript"),
("mjs", "javascript"),
("cjs", "javascript"),
("typescript", "typescript"),
("typescriptreact", "typescript"),
("ts", "typescript"),
("tsx", "typescript"),
("mts", "typescript"),
("cts", "typescript"),
("markdown", "fallback"),
("md", "fallback"),
];
pub(crate) const SUPPORTED_FORMATS: [&str; 19] = [
"json",
"jsonc",
"yaml",
"csv",
"tsv",
"toml",
"ini",
"env",
"python",
"rust",
"go",
"shellscript",
"php",
"ruby",
"perl",
"csharp",
"javascript",
"typescript",
"fallback",
];
pub(crate) const FALLBACK_FORMAT: &str = "fallback";
fn normalise(value: &str) -> String {
super::text::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::text::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"),
("py", "python"),
("rs", "rust"),
("bash", "shellscript"),
("rb", "ruby"),
("cs", "csharp"),
("javascriptreact", "javascript"),
("tsx", "typescript"),
("md", "fallback"),
] {
assert_eq!(resolve_format(Some(alias), None), expected, "{alias}");
}
}
#[test]
fn a_source_file_resolves_to_its_language() {
for (filename, expected) in [
("main.rs", "rust"),
("app.py", "python"),
("server.go", "go"),
("deploy.sh", "shellscript"),
("Widget.cs", "csharp"),
("index.tsx", "typescript"),
("README.md", "fallback"),
] {
assert_eq!(resolve_format(None, Some(filename)), expected, "{filename}");
}
}
#[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_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 ["klingon", "", "wat"] {
assert_eq!(resolve_format(Some(name), None), FALLBACK_FORMAT, "{name}");
}
assert_eq!(resolve_format(None, Some("notes.rtf")), FALLBACK_FORMAT);
assert_eq!(resolve_format(None, Some("Makefile")), FALLBACK_FORMAT);
assert_eq!(resolve_format(None, None), FALLBACK_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"
);
}
}
}