emjudge_judgecore/
settings.rs1#![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}