code_executor/runner/
mod.rs

1mod metrics;
2
3use std::{
4    fs,
5    io::Write,
6    path::{Path, PathBuf},
7    process::{Command, Stdio},
8};
9
10pub use metrics::*;
11
12use crate::{
13    CommandArgs, Error, Result, runner,
14    sandbox::{self, Sandbox},
15    util,
16};
17
18#[derive(Debug)]
19pub struct Runner<'a> {
20    pub main_file: &'a str,
21    pub compiler_args: Option<CommandArgs<'a>>,
22    pub sandbox_config: sandbox::Config<'a>,
23}
24
25impl Runner<'_> {
26    fn create_unique_project(&self, code: &str) -> Result<PathBuf> {
27        let project_path = util::generate_unique_path(code);
28
29        fs::create_dir_all(&project_path)?;
30
31        let mut main_file_path = project_path.clone();
32        main_file_path.push(self.main_file);
33
34        let mut main_file = fs::OpenOptions::new()
35            .create_new(true)
36            .write(true)
37            .open(&main_file_path)?;
38
39        main_file.write_all(code.as_bytes())?;
40
41        Ok(project_path)
42    }
43
44    /// Create a unique project using the hash of time and code
45    fn compile(&self, project_path: &Path) -> Result<()> {
46        let Some(CommandArgs {
47            binary: compiler,
48            args,
49        }) = self.compiler_args
50        else {
51            return Ok(());
52        };
53
54        let process = Command::new(compiler)
55            .args(args)
56            .current_dir(project_path)
57            .stderr(Stdio::piped())
58            .spawn()?;
59
60        let compilation_error = process.wait_with_output()?.stderr;
61
62        if !compilation_error.is_empty() {
63            return Err(Error::Compilation {
64                message: String::from_utf8(compilation_error)?,
65            });
66        }
67
68        Ok(())
69    }
70
71    pub fn run(
72        &self,
73        code: impl AsRef<str>,
74        input_path: impl AsRef<Path>,
75    ) -> Result<runner::metrics::Metrics> {
76        let project_path = self.create_unique_project(code.as_ref())?;
77        let output_path =
78            project_path.join(util::hash((input_path.as_ref(), "output")).to_string());
79        let error_path = project_path.join(util::hash((input_path.as_ref(), "error")).to_string());
80
81        self.compile(&project_path)?;
82
83        let sandbox = Sandbox::builder()
84            .project_path(project_path)
85            .config(self.sandbox_config.clone())
86            .input(input_path.as_ref())?
87            .output_path(&output_path)
88            .error_path(&error_path)
89            .build();
90
91        let sandbox = sandbox.spawn()?;
92        let result = sandbox.wait()?;
93
94        Ok(result)
95    }
96}