diff --git a/alvokanto/src/isolate.rs b/alvokanto/src/isolate.rs index 2cfe869cce94698d4d8cbd146f9cf02bfc4db3a1..7c92fc5cde159252a573129ecb9491878d465d72 100644 --- a/alvokanto/src/isolate.rs +++ b/alvokanto/src/isolate.rs @@ -203,7 +203,9 @@ pub fn run( }) .arg(format!("--processes={}", run_params.process_limit)) .arg(format!( - "--dir={}={}", in_data_dir.to_str().unwrap(), out_data_dir.to_str().unwrap() + "--dir={}={}", + in_data_dir.to_str().unwrap(), + out_data_dir.to_str().unwrap() )) .args(if run_params.restricted { vec![ @@ -271,14 +273,16 @@ pub fn run( "RE" => RunStatus::RuntimeError, "TO" => RunStatus::TimeLimitExceeded, "XX" => RunStatus::RuntimeError, - "SG" => if stats.status == RunStatus::Ok { - RunStatus::RuntimeError - } else { - stats.status - }, + "SG" => { + if stats.status == RunStatus::Ok { + RunStatus::RuntimeError + } else { + stats.status + } + } _ => RunStatus::RuntimeError, } - }, + } "cg-oom-killed" => stats.status = RunStatus::MemoryLimitExceeded, _ => {} } diff --git a/alvokanto/src/language.rs b/alvokanto/src/language.rs index 0dd68b283e9db63b65a5811d1eb3ecbdb5d01873..79e6414e7f3b236b6dcf92905c1a0e23c9dcd5f6 100644 --- a/alvokanto/src/language.rs +++ b/alvokanto/src/language.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use std::process::Command; fn no_transform(source_text: String, _source_name: String) -> String { - return source_text; + source_text } #[derive(Clone)] @@ -54,34 +54,38 @@ pub fn get_supported_languages() -> HashMap<String, LanguageParams> { order, suffix: ".cpp".into(), name: name.replace("{}", version), - compile: Compile::Command(&no_transform, CommandTuple { - binary_path, - args: vec![ - // Static linked, the judging process has no access to shared libraries - "-static".into(), - // ONLINE_JUDGE define as in Codeforces - "-DONLINE_JUDGE".into(), - // Link to the math library - "-lm".into(), - // Strip all symbols - "-s".into(), - // Use std - format!("-std={}", std), - // Define language used - "-x".into(), - x.into(), - // Level 2 optimization - "-O2".into(), - // Output to exe - "-o".into(), - "{}".into(), - // Input from source - "{.}".into(), - ], - }, "{}".into()), + compile: Compile::Command( + &no_transform, + CommandTuple { + binary_path, + args: vec![ + // Static linked, the judging process has no access to shared libraries + "-static".into(), + // ONLINE_JUDGE define as in Codeforces + "-DONLINE_JUDGE".into(), + // Link to the math library + "-lm".into(), + // Strip all symbols + "-s".into(), + // Use std + format!("-std={}", std), + // Define language used + "-x".into(), + x.into(), + // Level 2 optimization + "-O2".into(), + // Output to exe + "-o".into(), + "{}".into(), + // Input from source + "{.}".into(), + ], + }, + "{}".into(), + ), run: CommandTuple { binary_path: "{}".into(), - args: vec![] + args: vec![], }, process_limit: 1, } @@ -106,32 +110,36 @@ pub fn get_supported_languages() -> HashMap<String, LanguageParams> { order: 6, name: "Free Pascal".into(), suffix: ".pas".into(), - compile: Compile::Command(&no_transform, CommandTuple { - binary_path: "/usr/bin/fpc".into(), - args: vec![ - // Level 2 optimizations - "-O2".into(), - // Strip the symbols from the executable - "-Xs".into(), - // Link statically - "-XS".into(), - // Allow label and goto, support C++ style inline and C-style operators - "-Sgic".into(), - // Show warnings and notes - "-vwn".into(), - // Define the symbol - "-dONLINE_JUDGE".into(), - // Set stack size - "-Cs67107839".into(), - // Language mode: Delphi compatibility - "-Mdelphi".into(), - "{.}".into(), - "-o{}".into(), - ], - }, "{}".into()), + compile: Compile::Command( + &no_transform, + CommandTuple { + binary_path: "/usr/bin/fpc".into(), + args: vec![ + // Level 2 optimizations + "-O2".into(), + // Strip the symbols from the executable + "-Xs".into(), + // Link statically + "-XS".into(), + // Allow label and goto, support C++ style inline and C-style operators + "-Sgic".into(), + // Show warnings and notes + "-vwn".into(), + // Define the symbol + "-dONLINE_JUDGE".into(), + // Set stack size + "-Cs67107839".into(), + // Language mode: Delphi compatibility + "-Mdelphi".into(), + "{.}".into(), + "-o{}".into(), + ], + }, + "{}".into(), + ), run: CommandTuple { binary_path: "{}".into(), - args: vec![] + args: vec![], }, process_limit: 1, }, @@ -165,7 +173,7 @@ pub fn get_supported_languages() -> HashMap<String, LanguageParams> { "{.}".into(), ], }, - "{}.class".into() + "{}.class".into(), ), run: CommandTuple { binary_path: "/usr/bin/java".into(), diff --git a/alvokanto/src/main.rs b/alvokanto/src/main.rs index c58e50d0e1deb7180f0eaaef51dafd5e59c0206a..c7798934af4bc514048029b9ad46eaa73ef5347c 100644 --- a/alvokanto/src/main.rs +++ b/alvokanto/src/main.rs @@ -1,9 +1,9 @@ -use jughisto::job_protocol::{GetJobRequest, JobResult, job_result, job, Language}; -use jughisto::job_protocol::job_queue_client::JobQueueClient; +use chrono::Local; use jughisto::import_contest::format_width; +use jughisto::job_protocol::job_queue_client::JobQueueClient; +use jughisto::job_protocol::{job, job_result, GetJobRequest, JobResult, Language}; use std::path::PathBuf; use which::which; -use chrono::Local; use tonic::transport::channel::Channel; use tonic::Status; @@ -11,16 +11,16 @@ use tonic::Status; mod isolate; mod language; -use tokio::time::{sleep, Duration}; -use isolate::{IsolateBox, new_isolate_box, RunStatus, RunStats, CommandTuple, CompileParams}; -use std::fs::read_to_string; -use std::os::unix::fs::PermissionsExt; -use std::fs; -use log::info; +use isolate::{new_isolate_box, CommandTuple, CompileParams, IsolateBox, RunStats, RunStatus}; use language::Compile; +use log::info; use std::convert::TryInto; +use std::fs; +use std::fs::read_to_string; use std::fs::File; use std::io::Write; +use std::os::unix::fs::PermissionsExt; +use tokio::time::{sleep, Duration}; pub fn get_isolate_executable_path() -> PathBuf { which("isolate").expect("isolate binary not installed") @@ -34,13 +34,13 @@ fn run_cached( language: String, time_limit_ms: i32, memory_limit_kib: i32, - request: job::RunCached + request: job::RunCached, ) -> JobResult { info!("Starting to run"); let root_data = PathBuf::from("/data/"); let language = supported_languages.get(&language); - if let None = language { + if language.is_none() { return JobResult { uuid, code: job_result::Code::InvalidLanguage.into(), @@ -53,12 +53,11 @@ fn run_cached( let path_without_suffix = path_with_suffix.with_extension(""); if let Compile::Command(_, command, output) = &language.compile { - let output_path = - output.replace("{.}", path_with_suffix.to_str().unwrap()) - .replace("{}", path_without_suffix.to_str().unwrap()); + let output_path = output + .replace("{.}", path_with_suffix.to_str().unwrap()) + .replace("{}", path_without_suffix.to_str().unwrap()); if !root_data.join(&output_path).exists() { - isolate::reset(&isolate_executable_path, isolate_box.id) - .expect("Reset failed"); + isolate::reset(isolate_executable_path, isolate_box.id).expect("Reset failed"); fs_extra::dir::copy( path_with_suffix.parent().unwrap(), &isolate_box.path, @@ -69,17 +68,25 @@ fn run_cached( copy_inside: true, content_only: true, depth: 0, - } - ).unwrap(); + }, + ) + .unwrap(); let command = CommandTuple { binary_path: command.binary_path.clone(), args: command .args .iter() - .map(|c| - c.replace("{.}", path_with_suffix.file_name().unwrap().to_str().unwrap()) - .replace("{}", path_without_suffix.file_name().unwrap().to_str().unwrap())) + .map(|c| { + c.replace( + "{.}", + path_with_suffix.file_name().unwrap().to_str().unwrap(), + ) + .replace( + "{}", + path_without_suffix.file_name().unwrap().to_str().unwrap(), + ) + }) .collect(), }; @@ -96,7 +103,8 @@ fn run_cached( time_limit_ms: 25_000, command: &command, }, - ).unwrap(); + ) + .unwrap(); if match compile_stats { RunStats { @@ -117,48 +125,69 @@ fn run_cached( memory_kib: compile_stats.memory_kib.unwrap(), time_ms: compile_stats.time_ms.unwrap(), time_wall_ms: compile_stats.time_wall_ms.unwrap(), - error_output: read_to_string(compile_stats.stderr_path).unwrap_or("".into()), - })) - } + error_output: read_to_string(compile_stats.stderr_path) + .unwrap_or("".into()), + })), + }; } - fs::copy(&isolate_box.path.join( - output.replace("{.}", path_with_suffix.file_name().unwrap().to_str().unwrap()) - .replace("{}", path_without_suffix.file_name().unwrap().to_str().unwrap())), &output_path).unwrap(); + fs::copy( + isolate_box.path.join( + output + .replace( + "{.}", + path_with_suffix.file_name().unwrap().to_str().unwrap(), + ) + .replace( + "{}", + path_without_suffix.file_name().unwrap().to_str().unwrap(), + ), + ), + &output_path, + ) + .unwrap(); } } - let inside_path_with_suffix = PathBuf::from(format!("/data-{}/", uuid)).join(&request.source_path); + let inside_path_with_suffix = + PathBuf::from(format!("/data-{}/", uuid)).join(&request.source_path); let inside_path_without_suffix = inside_path_with_suffix.with_extension(""); let command = CommandTuple { - binary_path: language.run.binary_path.to_str().unwrap() + binary_path: language + .run + .binary_path + .to_str() + .unwrap() .replace("{.}", inside_path_with_suffix.to_str().unwrap()) - .replace("{}", inside_path_without_suffix.to_str().unwrap()).into(), - args: language.run + .replace("{}", inside_path_without_suffix.to_str().unwrap()) + .into(), + args: language + .run .args .iter() - .map(|c| - c.replace("{.}", inside_path_with_suffix.to_str().unwrap()) - .replace("{}", inside_path_without_suffix.to_str().unwrap())) + .map(|c| { + c.replace("{.}", inside_path_with_suffix.to_str().unwrap()) + .replace("{}", inside_path_without_suffix.to_str().unwrap()) + }) .chain(request.arguments) .collect(), }; - isolate::reset(&isolate_executable_path, isolate_box.id) - .expect("Reset failed"); + isolate::reset(isolate_executable_path, isolate_box.id).expect("Reset failed"); let run_stats = isolate::execute( - &isolate_executable_path, - &isolate_box, + isolate_executable_path, + isolate_box, &command, &isolate::ExecuteParams { uuid: &uuid, - memory_limit_kib: memory_limit_kib, - time_limit_ms: time_limit_ms, + memory_limit_kib, + time_limit_ms, stdin_path: request.stdin_path, process_limit: language.process_limit, }, - ).unwrap(); + ) + .unwrap(); if let Some(stdout_path) = request.stdout_path { fs::copy(run_stats.stdout_path, root_data.join(stdout_path)).unwrap(); @@ -171,14 +200,14 @@ fn run_cached( code: job_result::Code::Ok.into(), which: Some(job_result::Which::RunCached(job_result::RunCached { result: match run_stats.status { - RunStatus::Ok => - job_result::run_cached::Result::Ok.into(), - RunStatus::RuntimeError => - job_result::run_cached::Result::RuntimeError.into(), - RunStatus::TimeLimitExceeded => - job_result::run_cached::Result::TimeLimitExceeded.into(), - RunStatus::MemoryLimitExceeded => - job_result::run_cached::Result::MemoryLimitExceeded.into(), + RunStatus::Ok => job_result::run_cached::Result::Ok.into(), + RunStatus::RuntimeError => job_result::run_cached::Result::RuntimeError.into(), + RunStatus::TimeLimitExceeded => { + job_result::run_cached::Result::TimeLimitExceeded.into() + } + RunStatus::MemoryLimitExceeded => { + job_result::run_cached::Result::MemoryLimitExceeded.into() + } }, exit_code: run_stats.exit_code.unwrap_or(42), exit_signal: run_stats.exit_signal, @@ -195,12 +224,12 @@ fn compile_checker_if_necessary( isolate_box: &IsolateBox, supported_languages: &HashMap<String, language::LanguageParams>, uuid: &String, - request: &job::Judgement + request: &job::Judgement, ) -> Option<JobResult> { let root_data = PathBuf::from("/data/"); let checker_language = supported_languages.get(&request.checker_language); - if let None = checker_language { + if checker_language.is_none() { return Some(JobResult { uuid: uuid.to_string(), code: job_result::Code::InvalidLanguage.into(), @@ -212,12 +241,11 @@ fn compile_checker_if_necessary( let path_with_suffix = PathBuf::from("/data/").join(&request.checker_source_path); let path_without_suffix = path_with_suffix.with_extension(""); if let Compile::Command(_, command, output) = &checker_language.compile { - let output_path = - output.replace("{.}", path_with_suffix.to_str().unwrap()) - .replace("{}", path_without_suffix.to_str().unwrap()); + let output_path = output + .replace("{.}", path_with_suffix.to_str().unwrap()) + .replace("{}", path_without_suffix.to_str().unwrap()); if !root_data.join(&output_path).exists() { - isolate::reset(&isolate_executable_path, isolate_box.id) - .expect("reset to work"); + isolate::reset(isolate_executable_path, isolate_box.id).expect("reset to work"); fs_extra::dir::copy( path_with_suffix.parent().unwrap(), &isolate_box.path, @@ -228,17 +256,25 @@ fn compile_checker_if_necessary( copy_inside: true, content_only: true, depth: 0, - } - ).unwrap(); + }, + ) + .unwrap(); let command = CommandTuple { binary_path: command.binary_path.clone(), args: command .args .iter() - .map(|c| - c.replace("{.}", path_with_suffix.file_name().unwrap().to_str().unwrap()) - .replace("{}", path_without_suffix.file_name().unwrap().to_str().unwrap())) + .map(|c| { + c.replace( + "{.}", + path_with_suffix.file_name().unwrap().to_str().unwrap(), + ) + .replace( + "{}", + path_without_suffix.file_name().unwrap().to_str().unwrap(), + ) + }) .collect(), }; @@ -248,14 +284,15 @@ fn compile_checker_if_necessary( isolate_executable_path, isolate_box, CompileParams { - uuid: &uuid, + uuid, // 1GiB memory_limit_kib: 1_024 * 1_024, // 25 seconds time_limit_ms: 25_000, command: &command, }, - ).unwrap(); + ) + .unwrap(); if match compile_stats { RunStats { @@ -277,20 +314,39 @@ fn compile_checker_if_necessary( memory_kib: compile_stats.memory_kib.unwrap(), time_ms: compile_stats.time_ms.unwrap(), time_wall_ms: compile_stats.time_wall_ms.unwrap(), - error_output: read_to_string(compile_stats.stderr_path).unwrap_or("".into()), - judge_start_instant: Local::now().naive_utc().format("%Y-%m-%dT%H:%M:%S%.f").to_string(), - judge_end_instant: Local::now().naive_utc().format("%Y-%m-%dT%H:%M:%S%.f").to_string(), - })) - }) + error_output: read_to_string(compile_stats.stderr_path) + .unwrap_or("".into()), + judge_start_instant: Local::now() + .naive_utc() + .format("%Y-%m-%dT%H:%M:%S%.f") + .to_string(), + judge_end_instant: Local::now() + .naive_utc() + .format("%Y-%m-%dT%H:%M:%S%.f") + .to_string(), + })), + }); } - fs::copy(&isolate_box.path.join( - output.replace("{.}", path_with_suffix.file_name().unwrap().to_str().unwrap()) - .replace("{}", path_without_suffix.file_name().unwrap().to_str().unwrap())), &output_path).unwrap(); + fs::copy( + isolate_box.path.join( + output + .replace( + "{.}", + path_with_suffix.file_name().unwrap().to_str().unwrap(), + ) + .replace( + "{}", + path_without_suffix.file_name().unwrap().to_str().unwrap(), + ), + ), + &output_path, + ) + .unwrap(); } } - return None + None } fn judge( @@ -301,10 +357,10 @@ fn judge( language: String, time_limit_ms: i32, memory_limit_kib: i32, - request: job::Judgement + request: job::Judgement, ) -> JobResult { let language = supported_languages.get(&language); - if let None = language { + if language.is_none() { return JobResult { uuid, code: job_result::Code::InvalidLanguage.into(), @@ -318,9 +374,9 @@ fn judge( isolate_box, supported_languages, &uuid, - &request + &request, ) { - return res + return res; } let judge_start_instant = Local::now().naive_utc(); @@ -328,12 +384,13 @@ fn judge( let mut binary_file = request.source_text.as_bytes().to_vec(); if let Compile::Command(transform, command, _) = &language.compile { - isolate::reset(&isolate_executable_path, isolate_box.id) - .expect("Reset failed"); + isolate::reset(isolate_executable_path, isolate_box.id).expect("Reset failed"); { - let mut file = File::create(isolate_box.path.join(format!("x{}", language.suffix))).unwrap(); - file.write_all(transform(request.source_text, "x".into()).as_bytes()).unwrap(); + let mut file = + File::create(isolate_box.path.join(format!("x{}", language.suffix))).unwrap(); + file.write_all(transform(request.source_text, "x".into()).as_bytes()) + .unwrap(); } let command = CommandTuple { @@ -341,15 +398,16 @@ fn judge( args: command .args .iter() - .map(|c| + .map(|c| { c.replace("{.}", &format!("x{}", language.suffix)) - .replace("{}", "x".into())) + .replace("{}", "x") + }) .collect(), }; let compile_stats = isolate::compile( - &isolate_executable_path, - &isolate_box, + isolate_executable_path, + isolate_box, CompileParams { uuid: &uuid, // 1GiB @@ -384,29 +442,47 @@ fn judge( time_ms: compile_stats.time_ms.unwrap(), time_wall_ms: compile_stats.time_wall_ms.unwrap(), error_output: read_to_string(compile_stats.stderr_path).unwrap_or("".into()), - judge_start_instant: judge_start_instant.format("%Y-%m-%dT%H:%M:%S%.f").to_string(), + judge_start_instant: judge_start_instant + .format("%Y-%m-%dT%H:%M:%S%.f") + .to_string(), judge_end_instant: judge_end_instant.format("%Y-%m-%dT%H:%M:%S%.f").to_string(), - })) - } + })), + }; } - binary_file = fs::read(isolate_box.path.join(language.run.binary_path.to_str().unwrap() - .replace("{.}", &format!("x{}", language.suffix)) - .replace("{}", "x".into()))).unwrap(); + binary_file = fs::read( + isolate_box.path.join( + language + .run + .binary_path + .to_str() + .unwrap() + .replace("{.}", &format!("x{}", language.suffix)) + .replace("{}", "x"), + ), + ) + .unwrap(); } let mut last_execute_stats: Option<RunStats> = None; let command = CommandTuple { - binary_path: language.run.binary_path.to_str().unwrap() + binary_path: language + .run + .binary_path + .to_str() + .unwrap() .replace("{.}", &format!("x{}", language.suffix)) - .replace("{}", "x".into()).into(), - args: language.run + .replace("{}", "x") + .into(), + args: language + .run .args .iter() - .map(|c| - c.replace("{.}", &format!("x{}", language.suffix)) - .replace("{}", "x".into())) + .map(|c| { + c.replace("{.}", &format!("x{}", language.suffix)) + .replace("{}", "x") + }) .collect(), }; @@ -417,10 +493,8 @@ fn judge( let mut time_wall_ms = 0; for i in 1..request.test_count + 1 { - isolate::reset(&isolate_executable_path, isolate_box.id) - .expect("Reset failed"); - let stdin_path = - format_width(&request.test_pattern, i.try_into().unwrap()); + isolate::reset(isolate_executable_path, isolate_box.id).expect("Reset failed"); + let stdin_path = format_width(&request.test_pattern, i.try_into().unwrap()); let answer_path = format!("{}.a", stdin_path); info!( "Starting run {}/{} with test {:?}", @@ -434,14 +508,14 @@ fn judge( file.write_all(&binary_file).unwrap(); } let execute_stats = isolate::execute( - &isolate_executable_path, - &isolate_box, + isolate_executable_path, + isolate_box, &command, &isolate::ExecuteParams { process_limit: language.process_limit, uuid: &uuid, - memory_limit_kib: memory_limit_kib, - time_limit_ms: time_limit_ms, + memory_limit_kib, + time_limit_ms, stdin_path: Some(stdin_path.clone()), }, ) @@ -472,7 +546,8 @@ fn judge( // TODO: Support non-compile based languages let command = CommandTuple { binary_path: PathBuf::from(format!("/data-{}/", &uuid)) - .join(&request.checker_source_path).with_extension(""), + .join(&request.checker_source_path) + .with_extension(""), args: vec![ PathBuf::from(format!("/data-{}/", &uuid)) .join(&stdin_path) @@ -493,13 +568,13 @@ fn judge( info!("Executing checker: {:?}", command); let checker_stats = isolate::execute( - &isolate_executable_path, - &isolate_box, + isolate_executable_path, + isolate_box, &command, &isolate::ExecuteParams { uuid: &uuid, - memory_limit_kib: memory_limit_kib, - time_limit_ms: time_limit_ms, + memory_limit_kib, + time_limit_ms, stdin_path: None, process_limit: 1, }, @@ -524,75 +599,89 @@ fn judge( let last_execute_stats = last_execute_stats.unwrap(); return JobResult { - uuid: uuid, + uuid, code: job_result::Code::Ok.into(), - which: Some(job_result::Which::Judgement( - job_result::Judgement { - verdict: match last_execute_stats.status { - RunStatus::Ok => match failed_test { - 0 => job_result::judgement::Verdict::Accepted.into(), - _ => job_result::judgement::Verdict::WrongAnswer.into(), - }, - RunStatus::TimeLimitExceeded => job_result::judgement::Verdict::TimeLimitExceeded.into(), - RunStatus::MemoryLimitExceeded => job_result::judgement::Verdict::MemoryLimitExceeded.into(), - RunStatus::RuntimeError => job_result::judgement::Verdict::RuntimeError.into(), + which: Some(job_result::Which::Judgement(job_result::Judgement { + verdict: match last_execute_stats.status { + RunStatus::Ok => match failed_test { + 0 => job_result::judgement::Verdict::Accepted.into(), + _ => job_result::judgement::Verdict::WrongAnswer.into(), }, - failed_test, - exit_signal: last_execute_stats.exit_signal, - exit_code: last_execute_stats.exit_code.unwrap_or(42), - memory_kib, - time_ms, - time_wall_ms, - error_output: error_output.unwrap_or("".into()), - judge_start_instant: judge_start_instant.format("%Y-%m-%dT%H:%M:%S%.f").to_string(), - judge_end_instant: judge_end_instant.format("%Y-%m-%dT%H:%M:%S%.f").to_string(), - } - )) - } + RunStatus::TimeLimitExceeded => { + job_result::judgement::Verdict::TimeLimitExceeded.into() + } + RunStatus::MemoryLimitExceeded => { + job_result::judgement::Verdict::MemoryLimitExceeded.into() + } + RunStatus::RuntimeError => job_result::judgement::Verdict::RuntimeError.into(), + }, + failed_test, + exit_signal: last_execute_stats.exit_signal, + exit_code: last_execute_stats.exit_code.unwrap_or(42), + memory_kib, + time_ms, + time_wall_ms, + error_output: error_output.unwrap_or("".into()), + judge_start_instant: judge_start_instant + .format("%Y-%m-%dT%H:%M:%S%.f") + .to_string(), + judge_end_instant: judge_end_instant.format("%Y-%m-%dT%H:%M:%S%.f").to_string(), + })), + }; } async fn job_loop( isolate_executable_path: &PathBuf, isolate_box: &IsolateBox, supported_languages: &HashMap<String, language::LanguageParams>, - mut client: JobQueueClient<Channel> + mut client: JobQueueClient<Channel>, ) -> Result<(), Status> { loop { log::info!("Waiting for job"); - let job = client.get_job(GetJobRequest { - supported_languages: supported_languages.iter().map(|(key, language)| Language { - key: key.clone(), - name: language.name.clone(), - order: language.order, - }).collect(), - }).await?.into_inner(); + let job = client + .get_job(GetJobRequest { + supported_languages: supported_languages + .iter() + .map(|(key, language)| Language { + key: key.clone(), + name: language.name.clone(), + order: language.order, + }) + .collect(), + }) + .await? + .into_inner(); log::info!("Got job uuid={}", job.uuid); match job.which { Some(job::Which::Judgement(judgement_request)) => { - client.submit_job_result(judge( - isolate_executable_path, - isolate_box, - supported_languages, - job.uuid, - job.language, - job.time_limit_ms, - job.memory_limit_kib, - judgement_request - )).await?; - }, + client + .submit_job_result(judge( + isolate_executable_path, + isolate_box, + supported_languages, + job.uuid, + job.language, + job.time_limit_ms, + job.memory_limit_kib, + judgement_request, + )) + .await?; + } Some(job::Which::RunCached(run_request)) => { - client.submit_job_result(run_cached( - isolate_executable_path, - isolate_box, - supported_languages, - job.uuid, - job.language, - job.time_limit_ms, - job.memory_limit_kib, - run_request - )).await?; - }, + client + .submit_job_result(run_cached( + isolate_executable_path, + isolate_box, + supported_languages, + job.uuid, + job.language, + job.time_limit_ms, + job.memory_limit_kib, + run_request, + )) + .await?; + } None => { log::info!("Empty job!"); } @@ -609,28 +698,37 @@ async fn main() { let isolate_executable_path = get_isolate_executable_path(); log::info!("Found isolate at {:?}", isolate_executable_path); - let isolate_box = new_isolate_box(&isolate_executable_path, 0).expect("Couldn't create an isolate box"); + let isolate_box = + new_isolate_box(&isolate_executable_path, 0).expect("Couldn't create an isolate box"); log::info!("Created an isolate box at {:?}", isolate_box.path); let supported_languages = language::get_supported_languages(); log::info!("Loaded {} supported languages", supported_languages.len()); for (key, language) in supported_languages.iter() { - log::trace!("Supported language {} ({}): {}", key, language.order, language.name); + log::trace!( + "Supported language {} ({}): {}", + key, + language.order, + language.name + ); } loop { match JobQueueClient::connect("http://jughisto:50051").await { - Err(e) => { log::info!("Failed to connnect: {}, trying again in 3 seconds", e); }, + Err(e) => { + log::info!("Failed to connnect: {}, trying again in 3 seconds", e); + } Ok(client) => { log::info!("Connected to jughisto"); - match job_loop( + if let Err(e) = job_loop( &isolate_executable_path, &isolate_box, &supported_languages, - client - ).await { - Err(e) => { log::error!("On job loop {}, trying again in 3 seconds", e); }, - _ => {} + client, + ) + .await + { + log::error!("On job loop {}, trying again in 3 seconds", e); } } } diff --git a/src/broadcaster.rs b/src/broadcaster.rs index 72f148cbc4d37b4d18b4d371ae78f197932ac33a..9eb8460d5951c96965d39d6de82c88559c3e41ea 100644 --- a/src/broadcaster.rs +++ b/src/broadcaster.rs @@ -73,9 +73,7 @@ impl Broadcaster { pub fn new_client(&mut self) -> Client { let (tx, rx) = channel(100); - tx - .try_send(Bytes::from("data: connected\n\n")) - .unwrap(); + tx.try_send(Bytes::from("data: connected\n\n")).unwrap(); self.clients.push(tx); Client(rx) diff --git a/src/import_contest.rs b/src/import_contest.rs index cde9b7de5a0c535f36623ae3d4b4ba9c30ffd568..799b33b014f83263679f9f4a9f452a762357e117 100644 --- a/src/import_contest.rs +++ b/src/import_contest.rs @@ -420,7 +420,8 @@ pub fn import_file<R: Read + Seek>( Ok((contest, problems, zip, report)) } -#[must_use] pub fn format_width(pattern_path: &str, i: usize) -> String { +#[must_use] +pub fn format_width(pattern_path: &str, i: usize) -> String { lazy_static! { static ref WIDTH_REGEX: Regex = Regex::new(r"%0(\d)+d").unwrap(); } diff --git a/src/main.rs b/src/main.rs index 8e7e4136b14d51d3427a60b90cd85678bea60e67..87b7751385704619fcbef82ba75090e46eb60525 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,6 @@ use std::error::Error; use std::sync::Arc; use std::time::Duration; -use base64::prelude::*; use actix_files::Files; use actix_identity::IdentityMiddleware; use actix_session::storage::CookieSessionStore; @@ -14,6 +13,7 @@ use actix_web::web::Data; use actix_web::{http, middleware, web, App, HttpServer}; use actix_web_flash_messages::storage::CookieMessageStore; use actix_web_flash_messages::{FlashMessagesFramework, Level}; +use base64::prelude::*; use broadcaster::Broadcaster; use chrono_tz::Tz; use dashmap::DashMap; @@ -115,11 +115,12 @@ async fn main() -> Result<(), Box<dyn Error>> { std::env::set_var("RUST_LOG", "info"); env_logger::init(); - let private_key = BASE64_STANDARD.decode( - env::var("IDENTITY_SECRET_KEY") - .expect("IDENTITY_SECRET_KEY environment variable is not set"), - ) - .unwrap(); + let private_key = BASE64_STANDARD + .decode( + env::var("IDENTITY_SECRET_KEY") + .expect("IDENTITY_SECRET_KEY environment variable is not set"), + ) + .unwrap(); let database_url = env::var("DATABASE_URL").expect("DATABASE_URL environment variable is not set"); @@ -247,9 +248,8 @@ async fn main() -> Result<(), Box<dyn Error>> { })) .serve(addr) .map_err(Into::<Box<dyn Error>>::into), - update_database(update_dabase_sender.subscribe(), pool.clone()).map_err(Into::< - Box<dyn Error>, - >::into) + update_database(update_dabase_sender.subscribe(), pool.clone()) + .map_err(Into::<Box<dyn Error>>::into) )?; Ok(()) diff --git a/src/pages/create_contest.rs b/src/pages/create_contest.rs index ef82d8fc0c5d6d171f3afca37709a82c316fd778..3f057dbec296d4e9398f72e5ed8b4fd35b0a3507 100644 --- a/src/pages/create_contest.rs +++ b/src/pages/create_contest.rs @@ -223,14 +223,8 @@ async fn create_contest( problem::NewProblem { id: problem_id.clone(), name: metadata.names.name[0].value.clone(), - memory_limit_bytes: metadata.judging.testset[0] - .memory_limit - .parse() - .unwrap(), - time_limit_ms: metadata.judging.testset[0] - .time_limit - .parse() - .unwrap(), + memory_limit_bytes: metadata.judging.testset[0].memory_limit.parse().unwrap(), + time_limit_ms: metadata.judging.testset[0].time_limit.parse().unwrap(), checker_path: metadata.assets.checker.source.path.clone(), checker_language: map_codeforces_language(&metadata.assets.checker.r#type)?, validator_path: metadata.assets.validators.validator[0].source.path.clone(), @@ -240,10 +234,7 @@ async fn create_contest( main_solution_path: main_solution.path.clone(), main_solution_language: map_codeforces_language(&main_solution.r#type)?, test_pattern: metadata.judging.testset[0].input_path_pattern.clone(), - test_count: metadata.judging.testset[0] - .test_count - .parse() - .unwrap(), + test_count: metadata.judging.testset[0].test_count.parse().unwrap(), status: "compiled".into(), creation_instant: Local::now().naive_utc(), creation_user_id: logged_user.id, @@ -341,9 +332,9 @@ async fn create_contest( contest::NewContestProblems { label: problem_label .get(&problem_id_without_revision) - .ok_or_else(|| PageError::Validation( - "Arquivo não contém problemas listados".into(), - ))? + .ok_or_else(|| { + PageError::Validation("Arquivo não contém problemas listados".into()) + })? .to_string() .to_uppercase(), contest_id: contest.as_ref().unwrap().id, diff --git a/src/pages/get_about.rs b/src/pages/get_about.rs index b9c490b2aa303a5209142562d6ba9d05dcac6449..54f7c28ce89f50ef88cfb040b1444c739812b1b1 100644 --- a/src/pages/get_about.rs +++ b/src/pages/get_about.rs @@ -4,7 +4,7 @@ use crate::pages::prelude::*; async fn get_about(base: BaseContext, hb: Data<Handlebars<'_>>) -> PageResult { #[derive(Serialize)] struct Context { - base: BaseContext + base: BaseContext, } render(&hb, "about", &Context { base }) diff --git a/src/pages/get_contest_scoreboard_by_id.rs b/src/pages/get_contest_scoreboard_by_id.rs index e2c55da2dfb896cc85215af2ef850cdf8163394a..1e427aa94e3d52f66502be56381b020df6036e2c 100644 --- a/src/pages/get_contest_scoreboard_by_id.rs +++ b/src/pages/get_contest_scoreboard_by_id.rs @@ -59,13 +59,13 @@ async fn get_contest_scoreboard_by_id( }).collect(); scores.sort_by(|a, b| { ( - a.user_name != None, + a.user_name.is_some(), -a.solved_count, a.penalty, &a.user_name, ) .cmp(&( - b.user_name != None, + b.user_name.is_some(), -b.solved_count, b.penalty, &b.user_name, diff --git a/src/pages/mod.rs b/src/pages/mod.rs index cc9cec79fd07a33148e43a79a00c2ccb79e3a54f..b3f00885eb3ce7fd7842066ab1c13e7b7263a624 100644 --- a/src/pages/mod.rs +++ b/src/pages/mod.rs @@ -108,9 +108,9 @@ fn get_formatted_problem_by_contest_with_score( None => "*".into(), }) .unwrap_or_else(|| "".into()), - first_ac_submission_minutes: p.first_ac_submission_instant.and_then(|t| { - contest.start_instant.map(|cs| (t - cs).num_minutes()) - }), + first_ac_submission_minutes: p + .first_ac_submission_instant + .and_then(|t| contest.start_instant.map(|cs| (t - cs).num_minutes())), failed_submissions: p.failed_submissions, id: p.id, name: p.name.clone(), @@ -165,10 +165,14 @@ fn get_formatted_submissions( uuid: (&submission.uuid).into(), verdict: submission .verdict - .as_ref().map_or_else(|| "WJ".into(), String::from), + .as_ref() + .map_or_else(|| "WJ".into(), String::from), problem_label: contest_problem.label.clone(), submission_instant: format_utc_date_time(tz, submission.submission_instant), - error_output: submission.error_output.as_ref().map(std::convert::Into::into), + error_output: submission + .error_output + .as_ref() + .map(std::convert::Into::into), user_name: user.name.clone(), time_ms: submission.time_ms, memory: match submission.memory_kib { diff --git a/src/pages/prelude.rs b/src/pages/prelude.rs index df74491c032bb5b21ae539894ac5e877b2ae69ee..5323fa2833b0e6e11387ffc99ed9c90e19d649dc 100644 --- a/src/pages/prelude.rs +++ b/src/pages/prelude.rs @@ -150,7 +150,11 @@ pub fn redirect_to_referer(message: String, request: &HttpRequest) -> HttpRespon let referer = request .headers() .get("Referer") - .and_then(|h| h.to_str().ok()).map_or_else(|| env::var("BASE_URL").expect("BASE_URL environment variable is not set"), std::convert::Into::into); + .and_then(|h| h.to_str().ok()) + .map_or_else( + || env::var("BASE_URL").expect("BASE_URL environment variable is not set"), + std::convert::Into::into, + ); FlashMessage::info(message).send(); HttpResponse::SeeOther() .append_header((header::LOCATION, HeaderValue::from_str(&referer).unwrap()))