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}