eval_stack/
compile.rs

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}