1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*!
A submodule dedicated to parsing functions of the `State::AplusQuestionnaire` state.

Copyright © 2020 Santtu Söderholm
*/

use super::*;

/// A function for reading in blocks of intermediate text (rST paragraphs) between questionnaire questions.
pub fn parse_aplus_questionnaire_text(
    src_lines: &Vec<String>,
    base_indent: usize,
    section_level: &mut usize,
    line_cursor: &mut LineCursor,
    mut doctree: DocTree,
    captures: &regex::Captures,
    pattern_name: &Pattern,
) -> TransitionResult {

    let detected_indent = captures.get(1).unwrap().as_str().chars().count();

    match Parser::parent_indent_matches(doctree.shared_data(), detected_indent) {
        IndentationMatch::JustRight => {
            let start_line = line_cursor.relative_offset();
            let indent_allowed = true;
            let remove_indent = true;
            let alignment = Some(detected_indent);
            let (block_lines, offset) = if let TextBlockResult::Ok {lines, offset } = Parser::read_text_block(
                src_lines,
                start_line,
                indent_allowed,
                remove_indent,
                alignment,
                true
            ) {
                (lines, offset)
            } else {
                panic!("Error when reading intermediate text in A+ questionnaire on line {}. Computer says no...", line_cursor.sum_total())
            };

            let inline_nodes = match Parser::inline_parse(block_lines.join("\n"), None, line_cursor) {
                InlineParsingResult::Nodes(nodes) => nodes,
                _ => panic!(
                    "Could not parse intermediate questionnaire text on line {} for inline nodes. Computer says no...",
                    line_cursor.sum_total()
                )
            };

            let paragraph = TreeNodeType::Paragraph {
                indent: detected_indent,
            };
            doctree = match doctree.push_data_and_focus(paragraph) {
                Ok(tree) => tree,
                Err(tree) => {
                    return TransitionResult::Failure {
                        message: format!(
                            "Node insertion error on line {}. Computer says no...",
                            line_cursor.sum_total()
                        ),
                        doctree: tree,
                    }
                }
            };
            for node in inline_nodes {
                doctree = match doctree.push_data(node) {
                    Ok(tree) => tree,
                    Err(tree) => {
                        return TransitionResult::Failure {
                            message: format!(
                                "Node insertion error on line {}. Computer says no...",
                                line_cursor.sum_total()
                            ),
                            doctree: tree,
                        }
                    }
                };
            }
            doctree = doctree.focus_on_parent();
            return TransitionResult::Success {
                doctree: doctree,
                push_or_pop: PushOrPop::Neither,
                line_advance: LineAdvance::Some(1),
            };
        }
        _ => {
            doctree = doctree.focus_on_parent();
            return TransitionResult::Success {
                doctree: doctree,
                push_or_pop: PushOrPop::Pop,
                line_advance: LineAdvance::None,
            };
        }
    }
}

/// Parses an A+ questionnaire directive. Works as an entry point between the parser and the functions
/// `crate::parser::directive_parsers::{parse_aplus_pick_any, parse_aplus_pick_one, parse_aplus_freetext}`.
pub fn parse_aplus_questionnaire_directive(
    src_lines: &Vec<String>,
    base_indent: usize,
    section_level: &mut usize,
    line_cursor: &mut LineCursor,
    mut doctree: DocTree,
    captures: &regex::Captures,
    pattern_name: &Pattern,
) -> TransitionResult {

    let detected_marker_indent = captures.get(1).unwrap().as_str().chars().count() + base_indent;
    let detected_directive_label = captures
        .get(2)
        .unwrap()
        .as_str()
        .split_whitespace()
        .collect::<String>()
        .to_lowercase();
    let detected_first_indent = captures.get(0).unwrap().as_str().chars().count() + base_indent;

    let empty_after_marker: bool = {
        let line = src_lines.get(line_cursor.relative_offset()).unwrap(); // Unwrapping is not a problem here.

        match line.char_indices().nth(detected_first_indent) {
            Some((index, _)) => line[index..].trim().is_empty(),
            None => true,
        }
    };

    let (body_indent, body_offset) =
        match Parser::indent_on_subsequent_lines(src_lines, line_cursor.relative_offset() + 1) {
            Some((indent, offset)) => (indent + base_indent, offset),
            None => (detected_first_indent, 0), // EOF encountered => stay on same line
        };

    match Parser::parent_indent_matches(doctree.shared_data(), detected_marker_indent) {
        IndentationMatch::JustRight => match detected_directive_label.as_str() {
            "pick-one" => directive_parsers::parse_aplus_pick_one(
                src_lines,
                doctree,
                line_cursor,
                detected_first_indent,
                body_indent,
                empty_after_marker,
            ),
            "pick-any" => directive_parsers::parse_aplus_pick_any(
                src_lines,
                doctree,
                line_cursor,
                detected_first_indent,
                body_indent,
                empty_after_marker,
            ),
            "freetext" => directive_parsers::parse_aplus_freetext(
                src_lines,
                doctree,
                line_cursor,
                detected_first_indent,
                body_indent,
                empty_after_marker,
            ),
            _ => {
                doctree = doctree.focus_on_parent();
                return TransitionResult::Success {
                    doctree: doctree,
                    push_or_pop: PushOrPop::Pop,
                    line_advance: LineAdvance::None,
                };
            }
        },
        _ => {
            doctree = doctree.focus_on_parent();
            return TransitionResult::Success {
                doctree: doctree,
                push_or_pop: PushOrPop::Pop,
                line_advance: LineAdvance::None,
            };
        }
    }
}