1mod language_set;
2use std::str::FromStr;
3
4pub use language_set::LanguageSet;
5
6use phf::{phf_map, phf_ordered_map};
7use serde::{Deserialize, Serialize};
8use strum::VariantNames;
9
10struct LanguageVersion {
11 build: Option<&'static str>,
12 run: &'static str,
13 install_command: Option<&'static str>,
14 init_command: Option<&'static str>,
15}
16
17struct Builtin {
18 builtin: BuiltInLanguage,
19 source_file: &'static str,
20 syntax: Syntax,
21 versions: phf::OrderedMap<&'static str, LanguageVersion>,
22}
23
24static BUILTINS: phf::Map<&'static str, Builtin> = phf_map! {
26 "python3" => Builtin {
27 builtin: BuiltInLanguage::Python3,
28 source_file: "solution.py",
29 syntax: Syntax::Python,
30 versions: phf_ordered_map! {
31 "latest" => LanguageVersion {
32 build: None,
33 run: "python3 ./solution.py",
34 install_command: Some("dnf install python3 -y"),
35 init_command: None,
36 }
37 },
38 },
39 "java" => Builtin {
40 builtin: BuiltInLanguage::Java,
41 source_file: "Solution.java",
42 syntax: Syntax::Java,
43 versions: phf_ordered_map! { "8" => LanguageVersion {
45 build: Some("javac Solution.java"),
46 run: "java Solution",
47 install_command: Some("dnf install java-1.8.0-openjdk-devel -y"),
48 init_command: None,
49 },
50 "11" => LanguageVersion {
51 build: Some("javac Solution.java"),
52 run: "java Solution",
53 install_command: Some("dnf install java-11-openjdk-devel -y"),
54 init_command: None,
55 },
56 "21" => LanguageVersion {
57 build: Some("javac Solution.java"),
58 run: "java Solution",
59 install_command: Some("dnf install java-21-openjdk-devel -y"),
60 init_command: None,
61 },
62 },
63 },
64 "javascript" => Builtin {
65 builtin: BuiltInLanguage::JavaScript,
66 source_file: "solution.js",
67 syntax: Syntax::Javascript,
68 versions: phf_ordered_map! {
69 "latest" => LanguageVersion {
70 build: None,
71 run: "nodejs solution.js",
72 install_command: Some("dnf install nodejs20 -y"),
73 init_command: None,
74 }
75 },
76 },
77 "rust" => Builtin {
78 builtin: BuiltInLanguage::Rust,
79 source_file: "solution.rs",
80 syntax: Syntax::Rust,
81 versions: phf_ordered_map! {
82 "latest" => LanguageVersion {
83 build: Some("rustc -o solution solution.rs"),
84 run: "./solution",
85 install_command: Some("dnf install rust -y"),
86 init_command: None,
87 }
88 },
89 },
90};
91
92#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, VariantNames)]
93#[strum(serialize_all = "lowercase")]
94pub enum BuiltInLanguage {
95 Python3,
96 Java,
97 JavaScript,
98 Rust,
99}
100
101impl BuiltInLanguage {
102 pub fn has_version(self, version: &Version) -> Result<(), Vec<&str>> {
103 let bil = &BUILTINS[self.name()];
104 match version {
105 Version::Latest => Ok(()),
106 Version::Specific(v) => {
107 if bil.versions.contains_key(v) {
108 Ok(())
109 } else {
110 Err(bil.versions.keys().copied().collect())
111 }
112 }
113 }
114 }
115
116 pub fn joined_variants() -> String {
117 BuiltInLanguage::VARIANTS
118 .iter()
119 .map(|s| format!("'{}'", s))
120 .collect::<Vec<_>>()
121 .join(", ")
122 }
123
124 pub const fn name(self) -> &'static str {
125 match self {
126 Self::Python3 => "python3",
127 Self::Java => "java",
128 Self::JavaScript => "javascript",
129 Self::Rust => "rust",
130 }
131 }
132
133 pub const fn display_name(self) -> &'static str {
134 match self {
135 Self::Python3 => "Python3",
136 Self::Java => "Java",
137 Self::JavaScript => "JavaScript",
138 Self::Rust => "Rust",
139 }
140 }
141
142 pub fn source_file(self) -> &'static str {
143 BUILTINS[self.name()].source_file
144 }
145
146 pub fn build_command(self, version: &Version) -> Option<&str> {
147 let bil = &BUILTINS[self.name()];
148 match version {
149 Version::Latest => bil.versions.values().last()?.build,
150 Version::Specific(v) => bil.versions[v].build,
151 }
152 }
153
154 pub fn run_command(self, version: &Version) -> &str {
155 let bil = &BUILTINS[self.name()];
156 match version {
157 Version::Latest => {
158 bil.versions
159 .values()
160 .last()
161 .expect("all language must have at least one version")
162 .run
163 }
164 Version::Specific(v) => bil.versions[v].run,
165 }
166 }
167
168 pub fn install_command(self, version: &Version) -> Option<&str> {
169 let bil = &BUILTINS[self.name()];
170 match version {
171 Version::Latest => {
172 bil.versions
173 .values()
174 .last()
175 .expect("all language must have at least one version")
176 .install_command
177 }
178 Version::Specific(v) => bil.versions[v].install_command,
179 }
180 }
181
182 pub fn init_command(self, version: &Version) -> Option<&str> {
183 let bil = &BUILTINS[self.name()];
184 match version {
185 Version::Latest => {
186 bil.versions
187 .values()
188 .last()
189 .expect("all language must have at least one version")
190 .init_command
191 }
192 Version::Specific(v) => bil.versions[v].init_command,
193 }
194 }
195
196 pub fn syntax(self) -> Syntax {
197 BUILTINS[self.name()].syntax
198 }
199}
200
201impl From<&str> for BuiltInLanguage {
202 fn from(value: &str) -> Self {
203 BUILTINS[value].builtin
204 }
205}
206
207impl FromStr for BuiltInLanguage {
208 type Err = ();
209
210 fn from_str(s: &str) -> Result<Self, Self::Err> {
211 BUILTINS.get(s).map(|b| b.builtin).ok_or(())
212 }
213}
214
215#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
216pub enum Version {
217 Latest,
218 Specific(String),
219}
220
221#[derive(
223 Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, Default,
224)]
225#[serde(rename_all = "snake_case")]
226pub enum Syntax {
227 Ada,
228 Basic,
229 Batchfile,
230 #[serde(alias = "c", alias = "cpp")]
231 CCpp,
232 Clojure,
233 Cobol,
234 Csharp,
235 D,
236 Dart,
237 Ejs,
238 Elixir,
239 Elm,
240 Erlang,
241 Forth,
242 Fortran,
243 Fsharp,
244 Golang,
245 Haskell,
246 Java,
247 Javascript,
248 Julia,
249 Kotlin,
250 Lisp,
251 Lua,
252 Mips,
253 Nim,
254 Nix,
255 Ocaml,
256 Odin,
257 Pascal,
258 Perl,
259 Php,
260 #[default]
261 PlainText,
262 Powershell,
263 Prolog,
264 Python,
265 R,
266 Ruby,
267 Rust,
268 Scala,
269 Scheme,
270 Sh,
271 Typescript,
272 Zig,
273}
274
275impl Syntax {
276 pub fn from_string<E: serde::de::Error>(s: impl AsRef<str>) -> Result<Self, E> {
277 Syntax::deserialize(serde::de::value::StrDeserializer::<E>::new(s.as_ref()))
278 }
279}
280
281#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
282pub enum Language {
283 BuiltIn {
284 language: BuiltInLanguage,
285 version: Version,
286 },
287 Custom {
288 name: String,
289 display_name: String,
290 build: Option<String>,
291 run: String,
292 source_file: String,
293 syntax: Syntax,
294 },
295}
296
297impl Language {
298 pub fn name(&self) -> &str {
299 match self {
300 Language::BuiltIn { language, .. } => language.name(),
301 Language::Custom { name, .. } => name,
302 }
303 }
304
305 pub fn display_name(&self) -> &str {
306 match self {
307 Language::BuiltIn { language, .. } => language.display_name(),
308 Language::Custom { display_name, .. } => display_name,
309 }
310 }
311
312 pub fn source_file(&self) -> &str {
313 match self {
314 Language::BuiltIn { language, .. } => language.source_file(),
315 Language::Custom { source_file, .. } => source_file,
316 }
317 }
318
319 pub fn build_command(&self) -> Option<&str> {
320 match self {
321 Language::BuiltIn { language, version } => language.build_command(version),
322 Language::Custom { build, .. } => build.as_deref(),
323 }
324 }
325
326 pub fn run_command(&self) -> &str {
327 match self {
328 Language::BuiltIn { language, version } => language.run_command(version),
329 Language::Custom { run, .. } => run,
330 }
331 }
332
333 pub fn install_command(&self) -> Option<&str> {
334 match self {
335 Language::BuiltIn { language, version } => language.install_command(version),
336 Language::Custom { .. } => None,
337 }
338 }
339
340 pub fn init_command(&self) -> Option<&str> {
341 match self {
342 Language::BuiltIn { language, version } => language.init_command(version),
343 Language::Custom { .. } => None,
344 }
345 }
346
347 pub fn syntax(&self) -> Syntax {
348 match self {
349 Language::BuiltIn { language, .. } => language.syntax(),
350 Language::Custom { syntax, .. } => *syntax,
351 }
352 }
353}