run-kit 0.7.1

Universal multi-language runner and smart REPL
Documentation
use std::collections::HashMap;

use once_cell::sync::Lazy;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LanguageSpec {
    original: String,
    canonical: String,
}

impl LanguageSpec {
    pub fn new(token: impl Into<String>) -> Self {
        let raw = token.into();
        let canonical = canonical_language_id(&raw);
        Self {
            original: raw,
            canonical,
        }
    }

    pub fn canonical_id(&self) -> &str {
        &self.canonical
    }

    pub fn original(&self) -> &str {
        &self.original
    }
}

impl std::fmt::Display for LanguageSpec {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.original.eq_ignore_ascii_case(&self.canonical) {
            write!(f, "{}", self.canonical)
        } else {
            write!(f, "{} ({})", self.canonical, self.original)
        }
    }
}

static ALIASES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
    let pairs: &[(&str, &str)] = &[
        ("python", "python"),
        ("py", "python"),
        ("py3", "python"),
        ("python3", "python"),
        ("rust", "rust"),
        ("rs", "rust"),
        ("go", "go"),
        ("golang", "go"),
        ("csharp", "csharp"),
        ("cs", "csharp"),
        ("c#", "csharp"),
        ("dotnet", "csharp"),
        ("dotnetcore", "csharp"),
        ("typescript", "typescript"),
        ("ts", "typescript"),
        ("ts-node", "typescript"),
        ("javascript", "javascript"),
        ("js", "javascript"),
        ("node", "javascript"),
        ("nodejs", "javascript"),
        ("ecmascript", "javascript"),
        ("groovy", "groovy"),
        ("grv", "groovy"),
        ("groovysh", "groovy"),
        ("deno", "typescript"),
        ("denojs", "typescript"),
        ("ruby", "ruby"),
        ("rb", "ruby"),
        ("irb", "ruby"),
        ("lua", "lua"),
        ("luajit", "lua"),
        ("bash", "bash"),
        ("sh", "bash"),
        ("shell", "bash"),
        ("zsh", "bash"),
        ("java", "java"),
        ("c", "c"),
        ("cpp", "cpp"),
        ("c++", "cpp"),
        ("php", "php"),
        ("php-cli", "php"),
        ("kotlin", "kotlin"),
        ("kt", "kotlin"),
        ("kts", "kotlin"),
        ("r", "r"),
        ("rscript", "r"),
        ("cran", "r"),
        ("dart", "dart"),
        ("dartlang", "dart"),
        ("flutter", "dart"),
        ("swift", "swift"),
        ("swiftlang", "swift"),
        ("perl", "perl"),
        ("pl", "perl"),
        ("julia", "julia"),
        ("jl", "julia"),
        ("haskell", "haskell"),
        ("hs", "haskell"),
        ("ghci", "haskell"),
        ("elixir", "elixir"),
        ("ex", "elixir"),
        ("exs", "elixir"),
        ("iex", "elixir"),
        ("zig", "zig"),
        ("ziglang", "zig"),
        ("crystal", "crystal"),
        ("cr", "crystal"),
        ("crystal-lang", "crystal"),
        ("nim", "nim"),
        ("nimlang", "nim"),
    ];
    pairs.iter().cloned().collect()
});

pub fn canonical_language_id(token: &str) -> String {
    language_alias_lookup(token)
        .unwrap_or_else(|| token.trim())
        .to_ascii_lowercase()
}

pub fn language_alias_lookup(token: &str) -> Option<&'static str> {
    let normalized = token.trim().to_ascii_lowercase();
    ALIASES.get(normalized.as_str()).copied()
}

pub fn is_language_token(token: &str) -> bool {
    language_alias_lookup(token).is_some()
}

pub fn known_canonical_languages() -> Vec<&'static str> {
    let mut unique: Vec<_> = ALIASES.values().copied().collect();
    unique.sort_unstable();
    unique.dedup();
    unique
}