mapm/
template.rs

1//! Function for fetching templates
2
3use std::collections::HashMap;
4
5use crate::result::MapmErr::*;
6use crate::result::MapmResult;
7
8use std::fs;
9use std::path::Path;
10
11use serde::{Deserialize, Serialize};
12
13pub struct Template {
14    pub name: String,
15    pub engine: String,
16    /// A hashmap where the keys are the TeX input filenames and the values are the PDF output filenames.
17    pub texfiles: HashMap<String, String>,
18    pub problem_count: Option<usize>,
19    /// The number of answer choices each problem should have.
20    ///
21    /// This is an `Option` because if the template is not for a multiple choice test, `choices` can
22    /// take the value of `None`.
23    pub choices: Option<usize>,
24    /// Required variables for all contests with this template.
25    pub vars: Vec<String>,
26    /// Required variables for all problems in contests with this template.
27    pub problemvars: Vec<String>,
28    /// Required variables for all solutions of a problem in contests with this template.
29    pub solutionvars: Vec<String>,
30}
31
32#[derive(Debug, Serialize, Deserialize, Clone)]
33struct SerializedTemplate {
34    engine: String,
35    texfiles: HashMap<String, String>,
36    problem_count: Option<usize>,
37    choices: Option<usize>,
38    vars: Vec<String>,
39    problemvars: Vec<String>,
40    solutionvars: Vec<String>,
41}
42
43/// Gets a template config with the given name from the passed in directory
44
45pub fn fetch_template_config<T: AsRef<Path>>(
46    template_name: &str,
47    templates_dir: T,
48) -> MapmResult<Template> {
49    let templates_dir: &Path = templates_dir.as_ref();
50    let template_path = &templates_dir.join(template_name).join("config.yml");
51    match fs::read_to_string(template_path) {
52        Ok(template_yaml) => parse_template_yaml(template_name, &template_yaml),
53        Err(_) => Err(TemplateErr(format!(
54            "Could not read template `{}` from {:?}",
55            template_name, template_path,
56        ))),
57    }
58}
59
60fn parse_template_yaml(name: &str, yaml: &str) -> MapmResult<Template> {
61    match serde_yaml::from_str::<SerializedTemplate>(yaml) {
62        Ok(template) => Ok(Template {
63            name: String::from(name),
64            engine: template.engine,
65            texfiles: template.texfiles,
66            problem_count: template.problem_count,
67            choices: template.choices,
68            vars: template.vars,
69            problemvars: template.problemvars,
70            solutionvars: template.solutionvars,
71        }),
72        Err(err) => Err(TemplateErr(err.to_string())),
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    #[test]
79    fn test_parse() {
80        use super::parse_template_yaml;
81        use std::collections::HashMap;
82
83        let template_yaml = r#"engine: pdflatex
84texfiles:
85  problems.tex: "${title}.PDF"
86  solutions.tex: "${title}-sols.PDF"
87problem_count: 1
88vars:
89  - title
90  - year
91problemvars:
92  - problem
93solutionvars:
94  - text
95  - author
96"#;
97
98        let template = parse_template_yaml("template", template_yaml).unwrap();
99
100        let mut texfiles = HashMap::new();
101        texfiles.insert(String::from("problems.tex"), String::from("${title}.PDF"));
102        texfiles.insert(
103            String::from("solutions.tex"),
104            String::from("${title}-sols.PDF"),
105        );
106
107        assert_eq!(template.name, "template");
108        assert_eq!(template.engine, "pdflatex");
109        assert_eq!(template.texfiles, texfiles);
110        assert_eq!(
111            template.vars,
112            vec![String::from("title"), String::from("year")]
113        );
114        assert_eq!(template.problemvars, vec![String::from("problem")]);
115        assert_eq!(
116            template.solutionvars,
117            vec![String::from("text"), String::from("author")]
118        );
119    }
120}