Skip to main content

run/
language.rs

1use std::collections::HashMap;
2
3use once_cell::sync::Lazy;
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6pub struct LanguageSpec {
7    original: String,
8    canonical: String,
9}
10
11impl LanguageSpec {
12    pub fn new(token: impl Into<String>) -> Self {
13        let raw = token.into();
14        let canonical = canonical_language_id(&raw);
15        Self {
16            original: raw,
17            canonical,
18        }
19    }
20
21    pub fn canonical_id(&self) -> &str {
22        &self.canonical
23    }
24
25    pub fn original(&self) -> &str {
26        &self.original
27    }
28}
29
30impl std::fmt::Display for LanguageSpec {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        if self.original.eq_ignore_ascii_case(&self.canonical) {
33            write!(f, "{}", self.canonical)
34        } else {
35            write!(f, "{} ({})", self.canonical, self.original)
36        }
37    }
38}
39
40static ALIASES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
41    let pairs: &[(&str, &str)] = &[
42        ("python", "python"),
43        ("py", "python"),
44        ("py3", "python"),
45        ("python3", "python"),
46        ("rust", "rust"),
47        ("rs", "rust"),
48        ("go", "go"),
49        ("golang", "go"),
50        ("csharp", "csharp"),
51        ("cs", "csharp"),
52        ("c#", "csharp"),
53        ("dotnet", "csharp"),
54        ("dotnetcore", "csharp"),
55        ("typescript", "typescript"),
56        ("ts", "typescript"),
57        ("ts-node", "typescript"),
58        ("javascript", "javascript"),
59        ("js", "javascript"),
60        ("node", "javascript"),
61        ("nodejs", "javascript"),
62        ("ecmascript", "javascript"),
63        ("groovy", "groovy"),
64        ("grv", "groovy"),
65        ("groovysh", "groovy"),
66        ("deno", "typescript"),
67        ("denojs", "typescript"),
68        ("ruby", "ruby"),
69        ("rb", "ruby"),
70        ("irb", "ruby"),
71        ("lua", "lua"),
72        ("luajit", "lua"),
73        ("bash", "bash"),
74        ("sh", "bash"),
75        ("shell", "bash"),
76        ("zsh", "bash"),
77        ("java", "java"),
78        ("c", "c"),
79        ("cpp", "cpp"),
80        ("c++", "cpp"),
81        ("php", "php"),
82        ("php-cli", "php"),
83        ("kotlin", "kotlin"),
84        ("kt", "kotlin"),
85        ("kts", "kotlin"),
86        ("r", "r"),
87        ("rscript", "r"),
88        ("cran", "r"),
89        ("dart", "dart"),
90        ("dartlang", "dart"),
91        ("flutter", "dart"),
92        ("swift", "swift"),
93        ("swiftlang", "swift"),
94        ("perl", "perl"),
95        ("pl", "perl"),
96        ("julia", "julia"),
97        ("jl", "julia"),
98        ("haskell", "haskell"),
99        ("hs", "haskell"),
100        ("ghci", "haskell"),
101        ("elixir", "elixir"),
102        ("ex", "elixir"),
103        ("exs", "elixir"),
104        ("iex", "elixir"),
105        ("zig", "zig"),
106        ("ziglang", "zig"),
107        ("crystal", "crystal"),
108        ("cr", "crystal"),
109        ("crystal-lang", "crystal"),
110        ("nim", "nim"),
111        ("nimlang", "nim"),
112    ];
113    pairs.iter().cloned().collect()
114});
115
116pub fn canonical_language_id(token: &str) -> String {
117    resolve_language_alias(token)
118        .unwrap_or_else(|| token.trim().to_ascii_lowercase())
119}
120
121/// Resolve a token to a canonical language id using custom then built-in aliases.
122pub fn resolve_language_alias(token: &str) -> Option<String> {
123    if let Some(lang) = crate::aliases::custom_alias_lookup(token) {
124        return Some(lang);
125    }
126    builtin_alias_lookup(token).map(str::to_string)
127}
128
129pub fn builtin_alias_lookup(token: &str) -> Option<&'static str> {
130    let normalized = token.trim().to_ascii_lowercase();
131    ALIASES.get(normalized.as_str()).copied()
132}
133
134/// Backward-compatible built-in alias lookup.
135pub fn language_alias_lookup(token: &str) -> Option<&'static str> {
136    builtin_alias_lookup(token)
137}
138
139pub fn is_language_token(token: &str) -> bool {
140    resolve_language_alias(token).is_some()
141}
142
143pub fn known_canonical_languages() -> Vec<&'static str> {
144    let mut unique: Vec<_> = ALIASES.values().copied().collect();
145    unique.sort_unstable();
146    unique.dedup();
147    unique
148}
149
150pub fn known_language_aliases() -> Vec<(&'static str, &'static str)> {
151    let mut pairs: Vec<_> = ALIASES
152        .iter()
153        .map(|(alias, lang)| (*alias, *lang))
154        .collect();
155    pairs.sort_unstable_by(|a, b| a.0.cmp(b.0));
156    pairs
157}
158
159pub fn known_custom_language_aliases() -> Vec<(String, String)> {
160    crate::aliases::list_custom_aliases()
161}