Skip to main content

standout_input/questionnaire/
render.rs

1use std::fmt::Write as _;
2
3use super::definition::{Item, Questionnaire};
4
5pub(crate) const FORMAT_LINE: &str = "#! standout-answers 1";
6pub(crate) const QUESTIONNAIRE_PREFIX: &str = "#! questionnaire:";
7pub(crate) const FINGERPRINT_PREFIX: &str = "#! fingerprint:";
8pub(crate) const TAG_OPEN: &str = "<id:";
9
10const REPEAT_GUIDANCE: &str =
11    "(Add an item by copying one complete block - its heading line and its questions - below the last block, then answering the copy.)";
12
13fn tag(id: &str) -> String {
14    format!("{TAG_OPEN}{id}>")
15}
16
17impl Questionnaire {
18    pub fn render_answer_sheet(&self) -> String {
19        let mut out = String::new();
20        out.push_str(FORMAT_LINE);
21        out.push('\n');
22        let _ = writeln!(out, "{QUESTIONNAIRE_PREFIX} {}", self.id());
23        let _ = writeln!(out, "{FINGERPRINT_PREFIX} {}", self.fingerprint());
24        render_items(self.items(), "", &mut out);
25        out
26    }
27}
28
29fn render_items(items: &[Item], number_prefix: &str, out: &mut String) {
30    for (index, item) in items.iter().enumerate() {
31        let number = display_number(number_prefix, index + 1);
32        match item {
33            Item::Field(field) => {
34                out.push('\n');
35                let _ = writeln!(
36                    out,
37                    "{number} {} ({}) {}",
38                    field.prompt(),
39                    field.type_hint(),
40                    tag(field.id())
41                );
42                if let Some(default) = field.default() {
43                    let _ = writeln!(out, "{default}");
44                }
45            }
46            Item::Group(group) => {
47                let occurrences = group.repeat().map_or(1, |repeat| repeat.min());
48                for occurrence in 0..occurrences {
49                    out.push('\n');
50                    let _ = writeln!(
51                        out,
52                        "{number} {} ({}) {}",
53                        group.prompt(),
54                        group.type_hint(),
55                        tag(group.id())
56                    );
57                    if group.repeat().is_some() && occurrence == 0 {
58                        out.push_str(REPEAT_GUIDANCE);
59                        out.push('\n');
60                    }
61                    render_items(group.children(), number.trim_end_matches('.'), out);
62                }
63            }
64        }
65    }
66}
67
68fn display_number(prefix: &str, ordinal: usize) -> String {
69    if prefix.is_empty() {
70        format!("{ordinal}.")
71    } else {
72        format!("{prefix}.{ordinal}")
73    }
74}