Skip to content

Commit afe76a4

Browse files
Return verdict; add support for file io
1 parent f9c1518 commit afe76a4

File tree

3 files changed

+78
-21
lines changed

3 files changed

+78
-21
lines changed

src/compile_and_execute.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
44
use crate::{
55
compile::{compile, CompileRequest},
66
error::AppError,
7-
execute::{execute, ExecuteRequest},
8-
run_command::{CommandOptions, CommandOutput},
7+
execute::{execute, ExecuteOptions, ExecuteRequest, ExecuteResponse},
8+
run_command::CommandOutput,
99
};
1010

1111
/// Payload for POST /compile-and-execute
@@ -15,15 +15,15 @@ use crate::{
1515
#[derive(Deserialize)]
1616
pub struct CompileAndExecuteRequest {
1717
pub compile: CompileRequest,
18-
pub execute: CommandOptions,
18+
pub execute: ExecuteOptions,
1919
}
2020

2121
/// Response for POST /compile-and-execute
2222
#[derive(Serialize)]
2323
pub struct CompileAndExecuteResponse {
2424
pub compile: CommandOutput,
2525
/// None if the program failed to compile.
26-
pub execute: Option<CommandOutput>,
26+
pub execute: Option<ExecuteResponse>,
2727
}
2828

2929
pub async fn compile_and_execute_handler(

src/execute.rs

+74-10
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,81 @@ use std::{
66

77
use anyhow::{Result, anyhow};
88
use axum::Json;
9-
use serde::Deserialize;
9+
use serde::{Deserialize, Serialize};
1010
use tempdir::TempDir;
1111

1212
use crate::{
1313
error::AppError,
14-
run_command::{run_command, CommandOptions, CommandOutput},
14+
run_command::{run_command, CommandOptions},
1515
types::{Executable, Language},
1616
};
1717
use base64::{prelude::BASE64_STANDARD, Engine};
1818

1919
#[derive(Deserialize)]
2020
pub struct ExecuteRequest {
2121
pub executable: Executable,
22-
pub options: CommandOptions,
22+
pub options: ExecuteOptions,
2323
}
2424

25-
pub fn execute(payload: ExecuteRequest) -> Result<CommandOutput> {
25+
#[derive(Deserialize)]
26+
pub struct ExecuteOptions {
27+
pub stdin: String,
28+
pub timeout_ms: u32,
29+
30+
/// Alphanumeric string if you want file I/O to be supported, such as "cowdating".
31+
///
32+
/// Will create the files `file_io_name`.in and read `file_io_name`.out.
33+
pub file_io_name: Option<String>,
34+
}
35+
36+
#[derive(Serialize)]
37+
pub enum Verdict {
38+
#[serde(rename = "accepted")]
39+
Accepted,
40+
#[serde(rename = "wrong_answer")]
41+
#[allow(dead_code)]
42+
WrongAnswer,
43+
#[serde(rename = "time_limit_exceeded")]
44+
TimeLimitExceeded,
45+
#[serde(rename = "runtime_error")]
46+
RuntimeError,
47+
}
48+
49+
#[derive(Serialize)]
50+
pub struct ExecuteResponse {
51+
pub stdout: String,
52+
pub stderr: String,
53+
pub wall_time: String, // time format is 0:00.00
54+
pub memory_usage: String,
55+
56+
/// The underlying raw wait status. Note that this is different from an exit status.
57+
pub exit_code: i32,
58+
pub exit_signal: Option<String>,
59+
60+
pub verdict: Verdict,
61+
}
62+
63+
pub fn execute(payload: ExecuteRequest) -> Result<ExecuteResponse> {
2664
let tmp_dir = TempDir::new("execute")?;
2765

28-
match payload.executable {
66+
if let Some(name) = payload.options.file_io_name {
67+
if !name.chars().all(|c| c.is_ascii_alphanumeric()) {
68+
return Err(anyhow!("Invalid file I/O name. It must be alphanumeric, like \"cowdating\"."))
69+
}
70+
let mut stdin_file = File::create(tmp_dir.path().join(name).with_extension("in"))?;
71+
stdin_file.write_all(payload.options.stdin.as_ref())?;
72+
}
73+
74+
let command_options = CommandOptions { stdin: payload.options.stdin, timeout_ms: payload.options.timeout_ms };
75+
76+
let command_output = match payload.executable {
2977
Executable::Binary { value } => {
3078
let mut executable_file = File::create(tmp_dir.path().join("program"))?;
3179
executable_file.write_all(BASE64_STANDARD.decode(value)?.as_ref())?;
3280
executable_file.set_permissions(Permissions::from_mode(0o755))?;
3381
drop(executable_file);
3482

35-
run_command("./program", tmp_dir.path(), payload.options)
83+
run_command("./program", tmp_dir.path(), command_options)
3684
}
3785
Executable::JavaClass { class_name, value } => {
3886
let mut class_file = File::create(
@@ -47,7 +95,7 @@ pub fn execute(payload: ExecuteRequest) -> Result<CommandOutput> {
4795
run_command(
4896
format!("java {}", class_name).as_ref(),
4997
tmp_dir.path(),
50-
payload.options,
98+
command_options,
5199
)
52100
}
53101
Executable::Script {
@@ -63,13 +111,29 @@ pub fn execute(payload: ExecuteRequest) -> Result<CommandOutput> {
63111
executable_file.set_permissions(Permissions::from_mode(0o755))?;
64112
drop(executable_file);
65113

66-
run_command("python3.12 program.py", tmp_dir.path(), payload.options)
114+
run_command("python3.12 program.py", tmp_dir.path(), command_options)
67115
}
68-
}
116+
}?;
117+
118+
let verdict = match command_output.exit_code {
119+
124 => Verdict::TimeLimitExceeded,
120+
0 => Verdict::Accepted,
121+
_ => Verdict::RuntimeError,
122+
};
123+
124+
Ok(ExecuteResponse {
125+
stdout: command_output.stdout,
126+
stderr: command_output.stderr,
127+
wall_time: command_output.wall_time,
128+
memory_usage: command_output.memory_usage,
129+
exit_code: command_output.exit_code,
130+
exit_signal: command_output.exit_signal,
131+
verdict,
132+
})
69133
}
70134

71135
pub async fn execute_handler(
72136
Json(payload): Json<ExecuteRequest>,
73-
) -> Result<Json<CommandOutput>, AppError> {
137+
) -> Result<Json<ExecuteResponse>, AppError> {
74138
Ok(Json(execute(payload)?))
75139
}

src/run_command.rs

-7
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,7 @@ pub struct CommandOutput {
2626

2727
/// The underlying raw wait status. Note that this is different from an exit status.
2828
pub exit_code: i32,
29-
3029
pub exit_signal: Option<String>,
31-
// /**
32-
// * When executing, if `fileIOName` is given, this is
33-
// * set to whatever is written in `[fileIOName].out`
34-
// * or null if there's no such file.
35-
// */
36-
// pub file_output: Option<String>,
3730
}
3831

3932
struct TimingOutput {

0 commit comments

Comments
 (0)