@@ -6,33 +6,81 @@ use std::{
6
6
7
7
use anyhow:: { Result , anyhow} ;
8
8
use axum:: Json ;
9
- use serde:: Deserialize ;
9
+ use serde:: { Deserialize , Serialize } ;
10
10
use tempdir:: TempDir ;
11
11
12
12
use crate :: {
13
13
error:: AppError ,
14
- run_command:: { run_command, CommandOptions , CommandOutput } ,
14
+ run_command:: { run_command, CommandOptions } ,
15
15
types:: { Executable , Language } ,
16
16
} ;
17
17
use base64:: { prelude:: BASE64_STANDARD , Engine } ;
18
18
19
19
#[ derive( Deserialize ) ]
20
20
pub struct ExecuteRequest {
21
21
pub executable : Executable ,
22
- pub options : CommandOptions ,
22
+ pub options : ExecuteOptions ,
23
23
}
24
24
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 > {
26
64
let tmp_dir = TempDir :: new ( "execute" ) ?;
27
65
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 {
29
77
Executable :: Binary { value } => {
30
78
let mut executable_file = File :: create ( tmp_dir. path ( ) . join ( "program" ) ) ?;
31
79
executable_file. write_all ( BASE64_STANDARD . decode ( value) ?. as_ref ( ) ) ?;
32
80
executable_file. set_permissions ( Permissions :: from_mode ( 0o755 ) ) ?;
33
81
drop ( executable_file) ;
34
82
35
- run_command ( "./program" , tmp_dir. path ( ) , payload . options )
83
+ run_command ( "./program" , tmp_dir. path ( ) , command_options )
36
84
}
37
85
Executable :: JavaClass { class_name, value } => {
38
86
let mut class_file = File :: create (
@@ -47,7 +95,7 @@ pub fn execute(payload: ExecuteRequest) -> Result<CommandOutput> {
47
95
run_command (
48
96
format ! ( "java {}" , class_name) . as_ref ( ) ,
49
97
tmp_dir. path ( ) ,
50
- payload . options ,
98
+ command_options ,
51
99
)
52
100
}
53
101
Executable :: Script {
@@ -63,13 +111,29 @@ pub fn execute(payload: ExecuteRequest) -> Result<CommandOutput> {
63
111
executable_file. set_permissions ( Permissions :: from_mode ( 0o755 ) ) ?;
64
112
drop ( executable_file) ;
65
113
66
- run_command ( "python3.12 program.py" , tmp_dir. path ( ) , payload . options )
114
+ run_command ( "python3.12 program.py" , tmp_dir. path ( ) , command_options )
67
115
}
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
+ } )
69
133
}
70
134
71
135
pub async fn execute_handler (
72
136
Json ( payload) : Json < ExecuteRequest > ,
73
- ) -> Result < Json < CommandOutput > , AppError > {
137
+ ) -> Result < Json < ExecuteResponse > , AppError > {
74
138
Ok ( Json ( execute ( payload) ?) )
75
139
}
0 commit comments