atcoder_auto_tester/
runner.rs

1use 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}