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#[derive(Debug, Clone)]
10pub struct Answer {
11 pub fraction: i8,
13 pub text: String,
15 pub feedback: Option<String>,
17 pub text_format: TextFormat,
19}
20
21impl Answer {
22 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 pub fn set_text_format(&mut self, text_format: TextFormat) {
38 self.text_format = text_format;
39 }
40 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}