summaryrefslogtreecommitdiff
path: root/src/md/visitor/structure.rs
blob: d8faef65e9e77d021c28bdc27ce9b6c7e080e862 (plain)
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
use crate::md::panhelper;

use pandoc_ast::{Block, Inline, MutVisitor};

// A structure element in the document: a heading or a scenario snippet.
#[derive(Debug)]
pub enum Element {
    // Headings consist of the text and the level of the heading.
    Heading(String, i64),

    // Scenario snippets consist just of the unparsed text.
    Snippet(String),
}

impl Element {
    pub fn heading(text: &str, level: i64) -> Element {
        Element::Heading(text.to_string(), level)
    }

    pub fn snippet(text: &str) -> Element {
        Element::Snippet(text.to_string())
    }
}

// A MutVisitor for extracting document structure.
pub struct StructureVisitor {
    pub elements: Vec<Element>,
}

impl StructureVisitor {
    pub fn new() -> Self {
        Self { elements: vec![] }
    }
}

impl MutVisitor for StructureVisitor {
    fn visit_vec_block(&mut self, vec_block: &mut Vec<Block>) {
        use panhelper::is_class;
        for block in vec_block {
            match block {
                Block::Header(level, _attr, inlines) => {
                    let text = join(inlines);
                    let heading = Element::heading(&text, *level);
                    self.elements.push(heading);
                }
                Block::CodeBlock(attr, s) => {
                    if is_class(attr, "scenario") {
                        let snippet = Element::snippet(s);
                        self.elements.push(snippet);
                    }
                }
                _ => {
                    self.visit_block(block);
                }
            }
        }
    }
}

fn join(vec: &[Inline]) -> String {
    let mut buf = String::new();
    join_into_buffer(vec, &mut buf);
    buf
}

fn join_into_buffer(vec: &[Inline], buf: &mut String) {
    for item in vec {
        match item {
            Inline::Str(s) => buf.push_str(s),
            Inline::Emph(v) => join_into_buffer(v, buf),
            Inline::Strong(v) => join_into_buffer(v, buf),
            Inline::Strikeout(v) => join_into_buffer(v, buf),
            Inline::Superscript(v) => join_into_buffer(v, buf),
            Inline::Subscript(v) => join_into_buffer(v, buf),
            Inline::SmallCaps(v) => join_into_buffer(v, buf),
            Inline::Quoted(qt, v) => {
                let q = match qt {
                    pandoc_ast::QuoteType::SingleQuote => "'",
                    pandoc_ast::QuoteType::DoubleQuote => "\"",
                };
                buf.push_str(q);
                join_into_buffer(v, buf);
                buf.push_str(q);
            }
            Inline::Cite(_, v) => join_into_buffer(v, buf),
            Inline::Code(_attr, s) => buf.push_str(s),
            Inline::Space => buf.push(' '),
            Inline::SoftBreak => buf.push(' '),
            Inline::LineBreak => buf.push(' '),
            Inline::Math(_, s) => buf.push_str(s),
            Inline::RawInline(_, s) => buf.push_str(s),
            Inline::Link(_, v, _) => join_into_buffer(v, buf),
            Inline::Image(_, v, _) => join_into_buffer(v, buf),
            Inline::Note(_) => buf.push_str(""),
            Inline::Span(_attr, v) => join_into_buffer(v, buf),
            #[cfg(feature = "pandoc_ast_08")]
            Inline::Underline(v) => join_into_buffer(v, buf),
        }
    }
}