@@ -6,33 +6,81 @@ use std::{
66
77use anyhow:: { Result , anyhow} ;
88use axum:: Json ;
9- use serde:: Deserialize ;
9+ use serde:: { Deserialize , Serialize } ;
1010use tempdir:: TempDir ;
1111
1212use crate :: {
1313 error:: AppError ,
14- run_command:: { run_command, CommandOptions , CommandOutput } ,
14+ run_command:: { run_command, CommandOptions } ,
1515 types:: { Executable , Language } ,
1616} ;
1717use base64:: { prelude:: BASE64_STANDARD , Engine } ;
1818
1919#[ derive( Deserialize ) ]
2020pub 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
71135pub 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}
0 commit comments