atcoder_auto_tester/
runner.rs1use std::fs;
2use std::path;
3use std::process;
4
5use regex::Regex;
6
7use crate::args::Args;
8use crate::command::Command;
9use crate::config::Config;
10use crate::error::ErrorHandleable;
11use crate::message::Messageable;
12
13pub struct Runner {
14 args: Args,
15 config: Config,
16 file_regex: Regex,
17}
18
19impl Runner {
20 pub fn new(args: Args, config: Config) -> Self {
21 let pattern = config.file_name.replace("{}", r"([^\.]+)");
22 let file_regex = regex::Regex::new(&pattern).handle_error(&format!(
23 "Failed to parse a regular expression: {}",
24 pattern
25 ));
26 Runner {
27 args,
28 config,
29 file_regex,
30 }
31 }
32
33 pub fn run(&self, file_name: &str) -> Option<process::ExitStatus> {
34 self.match_file(&file_name).map(|task_name| {
35 Self::notify_detection(&file_name);
36 Self::create_test_directory(&self.args.test_directory);
37
38 let path = self.args.test_directory.join(task_name);
39 let url = self.config.task_url.replace("{}", task_name);
40 let command = format!(
41 "timeout {} {}",
42 self.args.timeout,
43 self.config.command.replace("{}", task_name)
44 );
45
46 if !path.exists() {
47 let exit_status = Self::download(&url, &path);
48 if !exit_status.success() {
49 return exit_status;
50 }
51 }
52 Self::test(&command, &path)
53 })
54 }
55
56 fn notify_detection(file_name: &str) {
57 println!();
58 format!("Detected change in file '{}' ", file_name)
59 .info_message()
60 .show();
61 }
62
63 fn match_file<'a>(&self, file_name: &'a str) -> Option<&'a str> {
64 self.file_regex
65 .captures(file_name)
66 .and_then(|caps| caps.get(1))
67 .map(|m| m.as_str())
68 }
69
70 fn create_test_directory(path: &path::Path) {
71 fs::create_dir_all(path).handle_error(&format!(
72 "Failed to create a directory: {}",
73 path.to_string_lossy()
74 ));
75 }
76
77 fn download(url: &str, path: &path::Path) -> process::ExitStatus {
78 Command::new("oj")
79 .arg("download")
80 .arg("--directory")
81 .arg(&path.to_string_lossy())
82 .arg(url)
83 .show()
84 .run()
85 }
86
87 fn test(command: &str, path: &path::Path) -> process::ExitStatus {
88 Command::new("oj")
89 .arg("test")
90 .arg("--command")
91 .arg(command)
92 .arg("--directory")
93 .arg(&path.to_string_lossy())
94 .show()
95 .run()
96 }
97}