emjudge_judgecore/
settings.rs

1#![allow(non_snake_case)]
2use config::Config;
3use serde::{Deserialize, Serialize};
4use serde_with::serde_as;
5use std::collections::HashMap;
6use std::io::Write;
7use std::process::{Command, Stdio};
8
9#[serde_as]
10#[derive(Clone, Debug, Default, Deserialize, Serialize)]
11pub struct CompileAndExeSetting {
12    #[serde(default = "CompileAndExeSetting::raw_code_default")]
13    pub raw_code: String,
14    #[serde(default = "CompileAndExeSetting::compile_command_default")]
15    pub compile_command: String,
16    #[serde(default = "CompileAndExeSetting::exe_command_default")]
17    pub exe_command: String,
18    #[serde(default = "CompileAndExeSetting::exe_files_default")]
19    pub exe_files: Vec<String>,
20    #[serde(default = "CompileAndExeSetting::language_info_command_default")]
21    pub language_info_command: String,
22}
23
24impl CompileAndExeSetting {
25    pub fn default() -> Self {
26        CompileAndExeSetting {
27            raw_code: Self::raw_code_default(),
28            compile_command: Self::compile_command_default(),
29            exe_command: Self::exe_command_default(),
30            exe_files: Self::exe_files_default(),
31            language_info_command: Self::language_info_command_default(),
32        }
33    }
34    fn raw_code_default() -> String {
35        String::new()
36    }
37    fn compile_command_default() -> String {
38        String::new()
39    }
40    fn exe_command_default() -> String {
41        String::new()
42    }
43    fn exe_files_default() -> Vec<String> {
44        vec![]
45    }
46    fn language_info_command_default() -> String {
47        String::new()
48    }
49}
50
51#[serde_as]
52#[derive(Clone, Debug, Default, Deserialize, Serialize)]
53pub struct CompileAndExeSettings {
54    #[serde(default = "CompileAndExeSettings::languages_default")]
55    pub languages: HashMap<String, CompileAndExeSetting>,
56}
57
58impl CompileAndExeSettings {
59    fn languages_default() -> HashMap<String, CompileAndExeSetting> {
60        HashMap::new()
61    }
62    pub fn default() -> Self {
63        CompileAndExeSettings {
64            languages: Self::languages_default(),
65        }
66    }
67
68    pub fn get_language_info(&self, language: &str) -> Result<String, String> {
69        let setting = match self.languages.get(language) {
70            None => {
71                return Err(format!("{} is not supported", language));
72            }
73            Some(result) => result,
74        };
75        let child = match Command::new("sh")
76            .arg("-c")
77            .arg(setting.language_info_command.as_str())
78            .stdout(Stdio::piped())
79            .stderr(Stdio::piped())
80            .spawn()
81        {
82            Ok(result) => result,
83            Err(result) => {
84                return Err(format!(
85                    "language_info_command failed: {}",
86                    result.to_string()
87                ));
88            }
89        };
90
91        let result = child.wait_with_output().unwrap();
92        if !result.status.success() {
93            return Err(format!(
94                "language_info_command failed: {}",
95                String::from_utf8(result.stderr).unwrap()
96            ));
97        }
98        if result.stdout.is_empty() {
99            return Ok(String::from_utf8(result.stderr).unwrap());
100        }
101        Ok(String::from_utf8(result.stdout).unwrap())
102    }
103
104    pub fn get_languages_info(&self) -> Result<HashMap<String, String>, String> {
105        let mut result = HashMap::new();
106        for (language, _) in self.languages.iter() {
107            match self.get_language_info(language) {
108                Ok(info) => {
109                    result.insert(language.clone(), info);
110                }
111                Err(result) => {
112                    return Err(result);
113                }
114            }
115        }
116        Ok(result)
117    }
118
119    pub fn self_check(&self) -> Result<(), String> {
120        for (language, setting) in self.languages.iter() {
121            if language.is_empty() {
122                return Err("language is empty".to_string());
123            }
124            if setting.exe_command.is_empty() {
125                return Err(format!("{}'s exe_command is empty", language));
126            }
127            if setting.exe_files.is_empty() {
128                return Err(format!("{}'s exe_files is empty", language));
129            }
130            if setting.language_info_command.is_empty() {
131                return Err(format!("{}'s language_info_command is empty", language));
132            }
133            if let Err(result) = self.get_language_info(language) {
134                return Err(format!(
135                    "{}'s language_info_command is invalid: {}",
136                    language, result
137                ));
138            }
139        }
140        Ok(())
141    }
142
143    pub fn load_from_string(s: String, format: config::FileFormat) -> Result<Self, String> {
144        match Config::builder()
145            .add_source(config::File::from_str(s.as_str(), format))
146            .build()
147        {
148            Ok(config) => match config.try_deserialize::<Self>() {
149                Ok(result) => match result.self_check() {
150                    Ok(_) => Ok(result),
151                    Err(result) => Err(result),
152                },
153                Err(result) => Err(result.to_string()),
154            },
155            Err(result) => Err(result.to_string()),
156        }
157    }
158
159    pub fn load_from_file(file_path: &str, format: config::FileFormat) -> Result<Self, String> {
160        match Config::builder()
161            .add_source(config::File::with_name(file_path).format(format))
162            .build()
163        {
164            Ok(config) => match config.try_deserialize::<Self>() {
165                Ok(result) => match result.self_check() {
166                    Ok(_) => Ok(result),
167                    Err(result) => Err(result),
168                },
169                Err(result) => Err(result.to_string()),
170            },
171            Err(result) => Err(result.to_string()),
172        }
173    }
174
175    pub fn store_to_file(&self, file_path: &str, format: config::FileFormat) {
176        std::fs::File::create(file_path)
177            .unwrap()
178            .write_all(self.format_to_string(format).as_bytes())
179            .unwrap();
180    }
181
182    pub fn format_to_string(&self, format: config::FileFormat) -> String {
183        match format {
184            config::FileFormat::Toml => toml::to_string(self).unwrap(),
185            config::FileFormat::Json => serde_json::to_string_pretty(self).unwrap(),
186            config::FileFormat::Yaml => serde_yaml::to_string(self).unwrap(),
187            config::FileFormat::Ini => toml_to_ini(&toml::to_string(self).unwrap()),
188            config::FileFormat::Ron => ron::to_string(self).unwrap(),
189            config::FileFormat::Json5 => json5::to_string(self).unwrap(),
190        }
191    }
192
193    pub fn get_language(&self, language: &str) -> Option<&CompileAndExeSetting> {
194        self.languages.get(language)
195    }
196}
197
198fn toml_to_ini(toml_string: &str) -> String {
199    let value: toml::Value = toml::from_str(toml_string).unwrap();
200    let mut ini_string = String::new();
201
202    for (section_name, section) in value.as_table().unwrap() {
203        ini_string.push_str(&format!("[{}]\n", section_name));
204
205        for (key, value) in section.as_table().unwrap() {
206            ini_string.push_str(&format!("{}={}\n", key, value));
207        }
208
209        ini_string.push('\n');
210    }
211
212    ini_string
213}
214
215pub fn create_a_tmp_user_return_uid(user_name: &str) -> Result<u32, ()> {
216    let _ = Command::new("adduser")
217        .arg("--disabled-password")
218        .arg("--gecos")
219        .arg("\"\"")
220        .arg("--force-badname")
221        .arg(user_name)
222        .stdout(Stdio::null())
223        .stderr(Stdio::null())
224        .spawn()
225        .unwrap()
226        .wait();
227    match users::get_user_by_name(user_name) {
228        None => {
229            return Err(());
230        }
231        Some(result) => Ok(result.uid()),
232    }
233}