provola-core 0.2.0

A tool for quick development/test cycle in any language
Documentation
use lazy_static::lazy_static;
use std::{fmt::Display, path::Path, str::FromStr};
use strum_macros::EnumIter;

#[derive(Debug, Copy, Clone, EnumIter, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
pub enum Language {
    Ada,
    Bash,
    C,
    Caml,
    CPlusPlus,
    CSharp,
    Clojure,
    Dart,
    Elixir,
    Erlang,
    FSharp,
    Go,
    Groovy,
    Haskell,
    Java,
    JavaScript,
    Kotlin,
    Lisp,
    ObjectiveC,
    PHP,
    Python,
    R,
    Ruby,
    Rust,
    Scala,
    Swift,
    TypeScript,
    VBA,
}

impl FromStr for Language {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use Language::*;

        let s = String::from(s);
        let s = s.to_lowercase();
        let s = s.as_str();

        match s {
            "ada" => Ok(Ada),
            "bash" => Ok(Bash),
            "c" => Ok(C),
            "caml" => Ok(Caml),
            "cpp" | "c++" | "cxx" | "cplusplus" => Ok(CPlusPlus),
            "c#" | "csharp" => Ok(CSharp),
            "clojure" => Ok(Clojure),
            "dart" => Ok(Dart),
            "elixir" => Ok(Elixir),
            "erlang" => Ok(Erlang),
            "f#" | "fsharp" => Ok(FSharp),
            "go" => Ok(Go),
            "groovy" => Ok(Groovy),
            "haskell" => Ok(Haskell),
            "java" => Ok(Java),
            "javascript" => Ok(JavaScript),
            "kotlin" => Ok(Kotlin),
            "lisp" => Ok(Lisp),
            "objectivec" => Ok(ObjectiveC),
            "php" => Ok(PHP),
            "python" => Ok(Python),
            "r" => Ok(R),
            "ruby" => Ok(Ruby),
            "rust" => Ok(Rust),
            "scala" => Ok(Scala),
            "swift" => Ok(Swift),
            "typescript" => Ok(TypeScript),
            "vba" => Ok(VBA),
            _ => Err("Invalid language".to_string()),
        }
    }
}

impl Display for Language {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let s = match &self {
            Language::Ada => "Ada",
            Language::Bash => "bash",
            Language::C => "C",
            Language::Caml => "caml",
            Language::CPlusPlus => "C++",
            Language::CSharp => "C#",
            Language::Clojure => "Clojure",
            Language::Dart => "dart",
            Language::Elixir => "elixir",
            Language::Erlang => "erlang",
            Language::FSharp => "f#",
            Language::Go => "Go",
            Language::Groovy => "Groovy",
            Language::Haskell => "Haskell",
            Language::Java => "Java",
            Language::JavaScript => "JavaScript",
            Language::Kotlin => "Kotlin",
            Language::Lisp => "Lisp",
            Language::ObjectiveC => "ObjectiveC",
            Language::PHP => "PHP",
            Language::Python => "Python",
            Language::R => "R",
            Language::Ruby => "Ruby",
            Language::Rust => "Rust",
            Language::Scala => "Scala",
            Language::Swift => "Swift",
            Language::TypeScript => "TypeScript",
            Language::VBA => "VBA",
        };

        write!(f, "{}", s)
    }
}

type Extensions = Vec<(&'static str, Language)>;

lazy_static! {
    static ref EXTENSIONS: Extensions = extensions();
}

fn extensions() -> Extensions {
    let mut xs = Vec::new();

    let mut add = |lang: Language, exts: &[&'static str]| {
        for &ext in exts {
            xs.push((ext, lang));
        }
    };

    add(Language::Bash, &["sh"]);
    add(Language::C, &["c", "c++", "cxx"]);
    add(Language::Haskell, &["hs"]);
    add(Language::Rust, &["rs"]);

    xs
}

impl Language {
    pub fn from_source(source: &Path) -> Option<Language> {
        source
            .extension()
            .and_then(|x| x.to_str())
            .and_then(|ext| EXTENSIONS.iter().find(|x| x.0 == ext))
            .map(|x| x.1)
    }
}

#[cfg(test)]
mod test {
    use std::path::PathBuf;

    use super::*;

    #[test]
    fn lang_from_string_to_string() {
        use strum::IntoEnumIterator;

        for lang in Language::iter() {
            let s = lang.to_string();
            let l = Language::from_str(&s).expect(&s);
            assert_eq!(lang, l);
        }
    }

    #[test]
    fn lang_from_source() {
        assert_eq!(
            Language::from_source(&PathBuf::from("foo.rs")),
            Some(Language::Rust)
        );
        assert_eq!(
            Language::from_source(&PathBuf::from("foo.hs")),
            Some(Language::Haskell)
        );
    }
}