1use std::{
2 env, ffi::OsStr, path::{Path, PathBuf}, process::Stdio
3};
4
5use anyhow::Result;
6use tokio::{fs::File, io, process::Command};
7
8#[derive(Default, Debug, Clone, Copy)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
11pub enum Language {
12 #[default]
13 Rust,
14 C,
15 CPP,
16 Python,
17 NodeJs,
18 Golang,
19 Java,
20}
21
22pub async fn compile<B: Into<PathBuf>, S: Into<PathBuf>, O: AsRef<str>>(
23 language: Language,
24 base: B,
25 source_file_path: S,
26 output_file: O,
27) -> Result<()> {
28 let base_path = Into::<PathBuf>::into(base);
29 let source_path = Into::<PathBuf>::into(source_file_path);
30 let source_path_str = source_path.to_string_lossy();
31 let output_path = base_path.join(output_file.as_ref());
32 let output_path_str = output_path.to_string_lossy();
33
34 let command = match language {
35 Language::C => {
36 let mut command = Command::new("gcc");
37 command.args([
38 "-O2",
39 "-w",
40 "-fmax-errors=3",
41 "-std=c17",
42 source_path_str.as_ref(),
43 "-lm",
44 "-o",
45 output_path_str.as_ref(),
46 ]);
47 Some(command)
48 }
49 Language::CPP => {
50 let mut command = Command::new("g++");
51 command.args([
52 "-O2",
53 "-w",
54 "-fmax-errors=3",
55 "-std=c++20",
56 source_path_str.as_ref(),
57 "-lm",
58 "-o",
59 output_path_str.as_ref(),
60 ]);
61 Some(command)
62 }
63 Language::Rust => {
64 let rustc_path = Path::new(&env::var("HOME")?)
65 .join(".rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/rustc");
66 if !rustc_path.exists() {
67 panic!("Failed to find rustc from {}", rustc_path.display());
68 }
69 let mut command = Command::new(rustc_path);
70 command.args([
71 "--edition=2021",
72 source_path_str.as_ref(),
73 "-C",
74 "embed-bitcode=no",
75 "-C",
76 "opt-level=2",
77 "-o",
78 output_file.as_ref(),
79 ]);
80 Some(command)
81 }
82 Language::Python => {
83 let mut command = Command::new("python3");
84 command.args(["-m", "py_compile", source_path_str.as_ref()]);
85 Some(command)
86 }
87 Language::NodeJs => None,
88 Language::Golang => {
89 let mut command = Command::new("go");
90 command.args([
91 "build",
92 "-o",
93 output_path_str.as_ref(),
94 source_path_str.as_ref(),
95 ]);
96 Some(command)
97 }
98 Language::Java => {
99 let mut command = Command::new("javac");
100 let java_path = base_path.join("Main.java");
101 if source_path.file_name() != Some(OsStr::new("Main.java")) {
102 io::copy(
103 &mut File::open(source_path_str.as_ref()).await?,
104 &mut File::create(&java_path).await?,
105 )
106 .await?;
107 }
108 command.arg(java_path.to_string_lossy().as_ref());
109 Some(command)
110 }
111 };
112
113 if let Some(mut command) = command {
114 command.kill_on_drop(true).stdout(Stdio::piped()).spawn()?;
115
116 let output = command.output().await?;
117
118 if !output.status.success() {
119 let error_message = String::from_utf8_lossy(&output.stderr).to_string();
120 return Err(anyhow::anyhow!(
121 "Failed to compile source code: {}",
122 error_message
123 ));
124 }
125 }
126 Ok(())
127}