thinlinelib/
config_parser.rs1use failure::{err_msg, Fallible};
2use run_script::ScriptOptions;
3use std::{env, fs::read_to_string};
4use yaml_rust::{Yaml, YamlLoader};
5
6trait Conversion {
9 fn to_string_vec(self) -> Vec<String>;
11}
12
13impl<'a> Conversion for Option<Vec<&'a str>> {
14 fn to_string_vec(self) -> Vec<String> {
15 if let Some(vec) = self {
16 vec.iter().map(|f| String::from(*f)).collect()
17 } else {
18 vec![]
19 }
20 }
21}
22
23trait ValueParser {
26 fn get_bool(&self, keys: &[&str], default: bool) -> bool;
28
29 fn get_str(&self, keys: &[&str]) -> Option<&str>;
31
32 fn get_str_vec(&self, keys: &[&str]) -> Option<Vec<&str>>;
34}
35
36impl ValueParser for Yaml {
37 fn get_bool(&self, keys: &[&str], default: bool) -> bool {
38 let mut yml_obj = self;
39
40 for key in keys {
41 if yml_obj[*key].is_badvalue() {
42 return default;
43 }
44
45 yml_obj = &yml_obj[*key];
46 if let Some(yml_bool) = yml_obj.as_bool() {
47 return yml_bool;
48 }
49
50 if yml_obj[0].is_badvalue() {
51 continue;
52 }
53 }
54
55 default
56 }
57
58 fn get_str(&self, keys: &[&str]) -> Option<&str> {
59 let mut yml_obj = self;
60
61 for key in keys {
62 if yml_obj[*key].is_badvalue() {
63 return None;
64 }
65
66 yml_obj = &yml_obj[*key];
67 if let Some(yml_str) = yml_obj.as_str() {
68 return Some(yml_str);
69 }
70
71 if yml_obj[0].is_badvalue() {
72 continue;
73 }
74
75 if let Some(yml_str) = yml_obj[0].as_str() {
76 return Some(yml_str);
77 }
78 }
79
80 None
81 }
82
83 fn get_str_vec(&self, keys: &[&str]) -> Option<Vec<&str>> {
84 let mut yml_obj = self;
85 let mut yml_vec: Vec<&str> = Vec::new();
86
87 for key in keys {
88 if yml_obj[*key].is_badvalue() {
89 return None;
90 }
91
92 yml_obj = &yml_obj[*key];
93 let mut i = 0;
94 while !yml_obj[i].is_badvalue() {
95 if let Some(yml_str) = yml_obj[i].as_str() {
96 yml_vec.push(yml_str);
97 }
98 i += 1;
99 }
100 }
101
102 if !yml_vec.is_empty() {
103 return Some(yml_vec);
104 }
105
106 None
107 }
108}
109
110#[derive(Default, Debug)]
113pub struct BuildScript {
114 pub log: bool,
116
117 pub windows: Vec<String>,
119
120 pub linux: Vec<String>,
122}
123
124impl BuildScript {
125 pub fn run(&self, dir: &str) -> Fallible<()> {
134 info!("Building target");
135
136 let current_working_dir = env::current_dir()?;
138
139 env::set_current_dir(&dir)?;
141
142 let mut options = ScriptOptions::new();
144
145 options.runner = if cfg!(target_os = "windows") {
147 Some(String::from("cmd"))
148 } else {
149 Some(String::from("sh"))
150 };
151
152 options.capture_output = !self.log;
154
155 let cmd = if cfg!(target_os = "windows") {
157 format!(r#"{}"#, self.windows.join(" && "))
158 } else {
159 format!(r#"{}"#, self.linux.join(" && "))
160 };
161
162 let (code, _, _) = run_script::run(cmd.as_str(), &vec![], &options)?;
164
165 env::set_current_dir(¤t_working_dir)?;
167
168 if code > 0 {
170 return Err(err_msg(format!(
171 "Executing build steps returned error code {}.",
172 code
173 )));
174 }
175
176 Ok(())
177 }
178}
179
180#[derive(Default, Debug)]
183pub struct ProjectParameters {
185 pub language: String,
187
188 pub test_env: String,
190
191 pub build_script: BuildScript,
193
194 pub lib_paths: Vec<String>,
196
197 pub source_dirs: Vec<String>,
199
200 pub include_dirs: Vec<String>,
202}
203
204impl ProjectParameters {
205 pub fn parse(yml: &str) -> Fallible<ProjectParameters> {
207 if let Ok(yml_params) = YamlLoader::load_from_str(read_to_string(yml)?.as_str()) {
208 if let Some(yml_param) = yml_params.get(0) {
209 let mut params = ProjectParameters::default();
210
211 params.language =
212 String::from(yml_param.get_str(&["language"]).ok_or_else(|| {
213 err_msg("Unable to get parameters for mandatory 'language'.")
214 })?);
215 params.test_env =
216 String::from(yml_param.get_str(&["test_env"]).ok_or_else(|| {
217 err_msg("Unable to get parameters for mandatory 'test_env'.")
218 })?);
219
220 params.source_dirs = yml_param.get_str_vec(&["analysis_dirs"]).to_string_vec();
221 params.include_dirs = yml_param.get_str_vec(&["include_dirs"]).to_string_vec();
222 params.build_script.log = yml_param.get_bool(&["build_script", "log"], true);
223 params.build_script.linux = yml_param
224 .get_str_vec(&["build_script", "linux"])
225 .to_string_vec();
226 params.build_script.windows = yml_param
227 .get_str_vec(&["build_script", "windows"])
228 .to_string_vec();
229 params.lib_paths = yml_param.get_str_vec(&["libs"]).to_string_vec();
230
231 return Ok(params);
232 }
233 }
234
235 Err(format_err!("Unable to parse project parameters."))
236 }
237}
238
239#[cfg(test)]
242mod value_parser {
243 use super::ValueParser;
244 use std::fs::read_to_string;
245 use std::path::Path;
246 use yaml_rust::YamlLoader;
247
248 #[test]
249 fn parse_yaml_config_bool_succeed() {
250 let yml_path = Path::new("tests")
251 .join("testdata")
252 .join("config")
253 .join("config1.yml");
254 let yml_params =
255 YamlLoader::load_from_str(read_to_string(yml_path).unwrap().as_str()).unwrap();
256 let yml_param = yml_params.get(0);
257
258 let build_log = yml_param.unwrap().get_bool(&["build_script", "log"], false);
259 assert_eq!(build_log, true);
260 }
261
262 #[test]
263 fn parse_yaml_config_bool_failed() {
264 let yml_path = Path::new("tests")
265 .join("testdata")
266 .join("config")
267 .join("config1.yml");
268 let yml_params =
269 YamlLoader::load_from_str(read_to_string(yml_path).unwrap().as_str()).unwrap();
270 let yml_param = yml_params.get(0);
271
272 let build_log = yml_param
273 .unwrap()
274 .get_bool(&["build_script", "none_existing"], true);
275 assert_eq!(build_log, true);
276 }
277
278 #[test]
279 fn parse_yaml_config_str_succeed() {
280 let yml_path = Path::new("tests")
281 .join("testdata")
282 .join("config")
283 .join("config1.yml");
284 let yml_params =
285 YamlLoader::load_from_str(read_to_string(yml_path).unwrap().as_str()).unwrap();
286 let yml_param = yml_params.get(0);
287
288 let test_env = yml_param.unwrap().get_str(&["test_env"]);
289 assert_eq!(test_env, Some("ctest"));
290
291 let language = yml_param.unwrap().get_str(&["language"]);
292 assert_eq!(language, Some("c"));
293 }
294
295 #[test]
296 fn parse_yaml_config_str_failed() {
297 let yml_path = Path::new("tests")
298 .join("testdata")
299 .join("config")
300 .join("config1.yml");
301 let yml_params =
302 YamlLoader::load_from_str(read_to_string(yml_path).unwrap().as_str()).unwrap();
303 let yml_param = yml_params.get(0);
304
305 {
306 let test_env = yml_param.unwrap().get_str(&["none_existing"]);
307 assert!(test_env.is_none());
308 }
309
310 {
311 let test_env = yml_param.unwrap().get_str(&[]);
312 assert!(test_env.is_none());
313 }
314
315 let yml_path = Path::new("tests")
316 .join("testdata")
317 .join("config")
318 .join("config4.yml");
319 let yml_params =
320 YamlLoader::load_from_str(read_to_string(yml_path).unwrap().as_str()).unwrap();
321 let yml_param = yml_params.get(0);
322
323 {
324 let test_env = yml_param.unwrap().get_str(&["test_env"]);
325 assert!(test_env.is_none());
326 }
327 }
328
329 #[test]
330 fn parse_yaml_config_str_vec_failed() {
331 let yml_path = Path::new("tests")
332 .join("testdata")
333 .join("config")
334 .join("config1.yml");
335 let yml_params =
336 YamlLoader::load_from_str(read_to_string(yml_path).unwrap().as_str()).unwrap();
337 let yml_param = yml_params.get(0);
338
339 {
340 let test_env = yml_param.unwrap().get_str_vec(&["include_dirs"]);
341 assert_eq!(test_env, Some(vec!["include", "src"]));
342 }
343
344 {
345 let test_env = yml_param.unwrap().get_str_vec(&["none_existing"]);
346 assert!(test_env.is_none());
347 }
348
349 {
350 let test_env = yml_param.unwrap().get_str_vec(&[]);
351 assert!(test_env.is_none());
352 }
353 }
354}