provola_core/
lang.rs

1use lazy_static::lazy_static;
2use std::{fmt::Display, path::Path, str::FromStr};
3use strum_macros::EnumIter;
4
5#[derive(Debug, Copy, Clone, EnumIter, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
6pub enum Language {
7    Ada,
8    Bash,
9    C,
10    Caml,
11    CPlusPlus,
12    CSharp,
13    Clojure,
14    Dart,
15    Elixir,
16    Erlang,
17    FSharp,
18    Go,
19    Groovy,
20    Haskell,
21    Java,
22    JavaScript,
23    Kotlin,
24    Lisp,
25    ObjectiveC,
26    PHP,
27    Python,
28    R,
29    Ruby,
30    Rust,
31    Scala,
32    Swift,
33    TypeScript,
34    VBA,
35}
36
37impl FromStr for Language {
38    type Err = String;
39
40    fn from_str(s: &str) -> Result<Self, Self::Err> {
41        use Language::*;
42
43        let s = String::from(s);
44        let s = s.to_lowercase();
45        let s = s.as_str();
46
47        match s {
48            "ada" => Ok(Ada),
49            "bash" => Ok(Bash),
50            "c" => Ok(C),
51            "caml" => Ok(Caml),
52            "cpp" | "c++" | "cxx" | "cplusplus" => Ok(CPlusPlus),
53            "c#" | "csharp" => Ok(CSharp),
54            "clojure" => Ok(Clojure),
55            "dart" => Ok(Dart),
56            "elixir" => Ok(Elixir),
57            "erlang" => Ok(Erlang),
58            "f#" | "fsharp" => Ok(FSharp),
59            "go" => Ok(Go),
60            "groovy" => Ok(Groovy),
61            "haskell" => Ok(Haskell),
62            "java" => Ok(Java),
63            "javascript" => Ok(JavaScript),
64            "kotlin" => Ok(Kotlin),
65            "lisp" => Ok(Lisp),
66            "objectivec" => Ok(ObjectiveC),
67            "php" => Ok(PHP),
68            "python" => Ok(Python),
69            "r" => Ok(R),
70            "ruby" => Ok(Ruby),
71            "rust" => Ok(Rust),
72            "scala" => Ok(Scala),
73            "swift" => Ok(Swift),
74            "typescript" => Ok(TypeScript),
75            "vba" => Ok(VBA),
76            _ => Err("Invalid language".to_string()),
77        }
78    }
79}
80
81impl Display for Language {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        let s = match &self {
84            Language::Ada => "Ada",
85            Language::Bash => "bash",
86            Language::C => "C",
87            Language::Caml => "caml",
88            Language::CPlusPlus => "C++",
89            Language::CSharp => "C#",
90            Language::Clojure => "Clojure",
91            Language::Dart => "dart",
92            Language::Elixir => "elixir",
93            Language::Erlang => "erlang",
94            Language::FSharp => "f#",
95            Language::Go => "Go",
96            Language::Groovy => "Groovy",
97            Language::Haskell => "Haskell",
98            Language::Java => "Java",
99            Language::JavaScript => "JavaScript",
100            Language::Kotlin => "Kotlin",
101            Language::Lisp => "Lisp",
102            Language::ObjectiveC => "ObjectiveC",
103            Language::PHP => "PHP",
104            Language::Python => "Python",
105            Language::R => "R",
106            Language::Ruby => "Ruby",
107            Language::Rust => "Rust",
108            Language::Scala => "Scala",
109            Language::Swift => "Swift",
110            Language::TypeScript => "TypeScript",
111            Language::VBA => "VBA",
112        };
113
114        write!(f, "{}", s)
115    }
116}
117
118type Extensions = Vec<(&'static str, Language)>;
119
120lazy_static! {
121    static ref EXTENSIONS: Extensions = extensions();
122}
123
124fn extensions() -> Extensions {
125    let mut xs = Vec::new();
126
127    let mut add = |lang: Language, exts: &[&'static str]| {
128        for &ext in exts {
129            xs.push((ext, lang));
130        }
131    };
132
133    add(Language::Bash, &["sh"]);
134    add(Language::C, &["c", "c++", "cxx"]);
135    add(Language::Haskell, &["hs"]);
136    add(Language::Rust, &["rs"]);
137
138    xs
139}
140
141impl Language {
142    pub fn from_source(source: &Path) -> Option<Language> {
143        source
144            .extension()
145            .and_then(|x| x.to_str())
146            .and_then(|ext| EXTENSIONS.iter().find(|x| x.0 == ext))
147            .map(|x| x.1)
148    }
149}
150
151#[cfg(test)]
152mod test {
153    use std::path::PathBuf;
154
155    use super::*;
156
157    #[test]
158    fn lang_from_string_to_string() {
159        use strum::IntoEnumIterator;
160
161        for lang in Language::iter() {
162            let s = lang.to_string();
163            let l = Language::from_str(&s).expect(&s);
164            assert_eq!(lang, l);
165        }
166    }
167
168    #[test]
169    fn lang_from_source() {
170        assert_eq!(
171            Language::from_source(&PathBuf::from("foo.rs")),
172            Some(Language::Rust)
173        );
174        assert_eq!(
175            Language::from_source(&PathBuf::from("foo.hs")),
176            Some(Language::Haskell)
177        );
178    }
179}