code_executor/runner/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
mod metrics;

use std::{
    fs,
    io::Write,
    path::{Path, PathBuf},
    process::{Command, Stdio},
};

pub use metrics::*;

use crate::{
    CommandArgs, Error, Result, runner,
    sandbox::{self, Sandbox},
    util,
};

#[derive(Debug)]
pub struct Runner<'a> {
    pub main_file: &'a str,
    pub compiler_args: Option<CommandArgs<'a>>,
    pub sandbox_config: sandbox::Config<'a>,
}

impl Runner<'_> {
    fn create_unique_project(&self, code: &str) -> Result<PathBuf> {
        let project_path = util::generate_unique_path(code);

        fs::create_dir_all(&project_path)?;

        let mut main_file_path = project_path.clone();
        main_file_path.push(self.main_file);

        let mut main_file = fs::OpenOptions::new()
            .create_new(true)
            .write(true)
            .open(&main_file_path)?;

        main_file.write_all(code.as_bytes())?;

        Ok(project_path)
    }

    /// Create a unique project using the hash of time and code
    fn compile(&self, project_path: &Path) -> Result<()> {
        let Some(CommandArgs {
            binary: compiler,
            args,
        }) = self.compiler_args
        else {
            return Ok(());
        };

        let process = Command::new(compiler)
            .args(args)
            .current_dir(project_path)
            .stderr(Stdio::piped())
            .spawn()?;

        let compilation_error = process.wait_with_output()?.stderr;

        if !compilation_error.is_empty() {
            return Err(Error::Compilation {
                message: String::from_utf8(compilation_error)?,
            });
        }

        Ok(())
    }

    pub fn run(
        &self,
        code: impl AsRef<str>,
        input_path: impl AsRef<Path>,
    ) -> Result<runner::metrics::Metrics> {
        let project_path = self.create_unique_project(code.as_ref())?;
        let output_path =
            project_path.join(util::hash((input_path.as_ref(), "output")).to_string());
        let error_path = project_path.join(util::hash((input_path.as_ref(), "error")).to_string());

        self.compile(&project_path)?;

        let sandbox = Sandbox::builder()
            .project_path(project_path)
            .config(self.sandbox_config.clone())
            .input(input_path.as_ref())?
            .output_path(&output_path)
            .error_path(&error_path)
            .build();

        let sandbox = sandbox.spawn()?;
        let result = sandbox.wait()?;

        Ok(result)
    }
}