moodle_xml/
answer.rs

1use std::fs::File;
2use xml::writer::{EventWriter, XmlEvent};
3
4use crate::question::TextFormat;
5use crate::quiz::QuizError;
6use crate::xml_util::{write_named_formatted_scope, write_text_tag};
7
8/// Answer type struct, which is common for all question types.
9#[derive(Debug, Clone)]
10pub struct Answer {
11    /// Fraction of the answer, -100-100
12    pub fraction: i8,
13    /// The answer text
14    pub text: String,
15    /// Optional feedback for the answer
16    pub feedback: Option<String>,
17    /// Text format for the answer and feedback, defines the rendering format for Moodle.
18    pub text_format: TextFormat,
19}
20
21impl Answer {
22    /// Generates a new Answer type struct
23    ///
24    /// ### Arguments
25    /// * `new_fraction` - The amount of points answer gives from 0-100
26    /// * `new_text` - Text displayed on the answer.
27    /// * `new_feedback` - Feedback displayed on the answer can be left empty with None.
28    pub fn new(new_fraction: i8, new_text: String, new_feedback: Option<String>) -> Self {
29        Self {
30            fraction: new_fraction,
31            text: new_text,
32            feedback: new_feedback,
33            text_format: TextFormat::default(),
34        }
35    }
36    /// Sets the text rendering format for the answer and feedback. Default is HTML.
37    pub fn set_text_format(&mut self, text_format: TextFormat) {
38        self.text_format = text_format;
39    }
40    /// Writes answer part of xml for EventWriter
41    pub(crate) fn to_xml(&self, writer: &mut EventWriter<&File>) -> Result<(), QuizError> {
42        if self.fraction > 100 {
43            return Err(QuizError::AnswerFractionError(
44                "Answer fraction is larger than 100".to_string(),
45            ));
46        }
47        writer.write(
48            XmlEvent::start_element("answer")
49                .attr("fraction", self.fraction.to_string().as_str())
50                .attr("format", self.text_format.name()),
51        )?;
52        write_text_tag(writer, self.text.as_str(), false)?;
53        if let Some(string) = self.feedback.as_ref() {
54            write_named_formatted_scope(writer, "feedback", self.text_format.into(), |writer| {
55                write_text_tag(writer, string, false)?;
56                Ok(())
57            })?;
58        }
59        writer.write(XmlEvent::end_element())?;
60        Ok(())
61    }
62}
63
64impl From<Answer> for Vec<Answer> {
65    fn from(answer: Answer) -> Self {
66        vec![answer]
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use std::io::{Read, Seek};
74    use xml::writer::EmitterConfig;
75
76    #[test]
77    fn test_single_answer() {
78        let mut tmp_file = tempfile::tempfile().unwrap();
79        let mut writer = EmitterConfig::new()
80            .perform_indent(true)
81            .create_writer(&tmp_file);
82
83        let mut answer1 = Answer::new(
84            100,
85            "Answer text".to_string(),
86            "Particularly well answered!".to_string().into(),
87        );
88        let mut answer2 = Answer::new(
89            -100,
90            "Answer text".to_string(),
91            "Ugh. Wrong answer!".to_string().into(),
92        );
93        answer1.set_text_format(TextFormat::Moodle);
94        answer1.to_xml(&mut writer).unwrap();
95        let mut buf = String::new();
96        tmp_file.seek(std::io::SeekFrom::Start(0)).unwrap();
97        tmp_file.read_to_string(&mut buf).unwrap();
98        let expected = r#"<?xml version="1.0" encoding="utf-8"?>
99<answer fraction="100" format="moodle_auto_format">
100  <text>Answer text</text>
101  <feedback format="moodle_auto_format">
102    <text>Particularly well answered!</text>
103  </feedback>
104</answer>"#;
105        assert_eq!(expected, buf);
106        buf.clear();
107        let mut tmp_file = tempfile::tempfile().unwrap();
108        let mut writer = EmitterConfig::new()
109            .perform_indent(true)
110            .create_writer(&tmp_file);
111        answer2.set_text_format(TextFormat::Moodle);
112        answer2.to_xml(&mut writer).unwrap();
113        let mut buf = String::new();
114        tmp_file.seek(std::io::SeekFrom::Start(0)).unwrap();
115        tmp_file.read_to_string(&mut buf).unwrap();
116        let expected = r#"<?xml version="1.0" encoding="utf-8"?>
117<answer fraction="-100" format="moodle_auto_format">
118  <text>Answer text</text>
119  <feedback format="moodle_auto_format">
120    <text>Ugh. Wrong answer!</text>
121  </feedback>
122</answer>"#;
123        assert_eq!(expected, buf);
124    }
125}