Skip to main content

strop_core/
languages.rs

1//! The canonical language metadata catalog (0051 §3): ONE in-repo table
2//! mapping file extensions to language names and back. The LSP registry
3//! and the query compiler both consume this — there is no second table.
4
5/// One language family: canonical name, filename extensions, documented
6/// value aliases (`rs` reads as `rust`).
7pub struct Language {
8    pub name: &'static str,
9    pub extensions: &'static [&'static str],
10    pub aliases: &'static [&'static str],
11}
12
13pub const LANGUAGES: &[Language] = &[
14    Language {
15        name: "rust",
16        extensions: &["rs"],
17        aliases: &["rs"],
18    },
19    Language {
20        name: "c",
21        extensions: &["c", "h"],
22        aliases: &[],
23    },
24    Language {
25        name: "cpp",
26        extensions: &["cpp", "cc", "cp", "cxx", "hpp", "hh", "hxx", "ino", "tpp"],
27        aliases: &["c++", "cxx"],
28    },
29    Language {
30        name: "lua",
31        extensions: &["lua"],
32        aliases: &["lua"],
33    },
34    Language {
35        name: "python",
36        extensions: &["py", "pyi", "pyw"],
37        aliases: &["py"],
38    },
39    Language {
40        name: "go",
41        extensions: &["go"],
42        aliases: &["golang"],
43    },
44    Language {
45        name: "javascript",
46        extensions: &["js", "jsx", "mjs", "cjs"],
47        aliases: &["js"],
48    },
49    Language {
50        name: "typescript",
51        extensions: &["ts", "tsx", "mts", "cts"],
52        aliases: &["ts"],
53    },
54    Language {
55        name: "json",
56        extensions: &["json", "jsonc"],
57        aliases: &[],
58    },
59    Language {
60        name: "shellscript",
61        extensions: &["sh", "bash", "zsh", "ksh"],
62        aliases: &["sh", "bash", "shell"],
63    },
64];
65
66/// Language name for a bare extension (no dot).
67pub fn language_for_extension_name(ext: &str) -> Option<&'static str> {
68    LANGUAGES
69        .iter()
70        .find(|language| language.extensions.contains(&ext))
71        .map(|language| language.name)
72}
73
74/// Language name for a dotted extension (`.rs`).
75pub fn language_for_extension(ext_with_dot: &str) -> Option<&'static str> {
76    language_for_extension_name(ext_with_dot.strip_prefix('.')?)
77}
78
79/// A language's extensions, by canonical name or documented alias.
80pub fn extensions_for_language(name_or_alias: &str) -> Option<&'static [&'static str]> {
81    LANGUAGES
82        .iter()
83        .find(|language| {
84            language.name == name_or_alias || language.aliases.contains(&name_or_alias)
85        })
86        .map(|language| language.extensions)
87}
88
89/// Every canonical language name (suggestions/discovery).
90pub fn language_names() -> impl Iterator<Item = &'static str> {
91    LANGUAGES.iter().map(|language| language.name)
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn lua_resolves_through_extension_name_and_alias() {
100        assert_eq!(language_for_extension(".lua"), Some("lua"));
101        assert_eq!(language_for_extension_name("lua"), Some("lua"));
102        assert_eq!(extensions_for_language("lua"), Some(&["lua"][..]));
103        assert!(language_names().any(|name| name == "lua"));
104    }
105
106    #[test]
107    fn ambiguous_headers_follow_one_policy() {
108        // One policy for ambiguous headers (0063 §3): plain .h is C;
109        // C++-spelled headers are cpp.
110        assert_eq!(language_for_extension(".h"), Some("c"));
111        for header in [".hpp", ".hh", ".hxx"] {
112            assert_eq!(language_for_extension(header), Some("cpp"));
113        }
114    }
115
116    #[test]
117    fn cpp_membership_covers_the_documented_extensions() {
118        assert_eq!(
119            extensions_for_language("c++"),
120            Some(&["cpp", "cc", "cp", "cxx", "hpp", "hh", "hxx", "ino", "tpp",][..])
121        );
122    }
123}