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
//! Helper trait for operations reading from the document tree.

use super::ast::Element;
use std::io;

/// Implements a traversion over a tree of `Element`.
///
/// All fields of the traversion struct can be mutated,
/// external settings cannot.
pub trait Traversion<'a, S: Copy + ?Sized> {
    /// push to the traversion path.
    fn path_push(&mut self, elem: &'a Element);
    /// pop from the traversion path.
    fn path_pop(&mut self) -> Option<&'a Element>;
    /// get the traversion path.
    fn get_path(&self) -> &Vec<&'a Element>;
    /// template method for handling single nodes.
    /// if the result is `false`, handling is complete and
    /// children of this node are not considered,
    /// otherwise `work()` is recursively called for all children.
    fn work(&mut self, _root: &'a Element, _settings: S, _out: &mut io::Write) -> io::Result<bool> {
        Ok(true)
    }

    /// template method for handling a vector of nodes.
    /// if the result is `false`, handling is complete and
    /// children of the vector's elements are not considered,
    /// otherwise `work()` is recursively called for all children.
    fn work_vec(
        &mut self,
        _root: &'a [Element],
        _settings: S,
        _out: &mut io::Write,
    ) -> io::Result<bool> {
        Ok(true)
    }

    /// run this traversion for a vector of elements.
    fn run_vec(
        &mut self,
        content: &'a [Element],
        settings: S,
        out: &mut io::Write,
    ) -> io::Result<()> {
        if !self.work_vec(content, settings, out)? {
            return Ok(());
        }
        for elem in &content[..] {
            self.run(elem, settings, out)?;
        }
        Ok(())
    }
    /// run this traversion for an element.
    fn run(&mut self, root: &'a Element, settings: S, out: &mut io::Write) -> io::Result<()> {
        self.path_push(root);

        // break if work function breaks recursion.
        if !self.work(root, settings, out)? {
            return Ok(());
        }
        match *root {
            Element::Document(ref e) => self.run_vec(&e.content, settings, out)?,
            Element::Formatted(ref e) => self.run_vec(&e.content, settings, out)?,
            Element::Paragraph(ref e) => self.run_vec(&e.content, settings, out)?,
            Element::ListItem(ref e) => self.run_vec(&e.content, settings, out)?,
            Element::List(ref e) => self.run_vec(&e.content, settings, out)?,
            Element::TableCell(ref e) => self.run_vec(&e.content, settings, out)?,
            Element::HtmlTag(ref e) => self.run_vec(&e.content, settings, out)?,
            Element::Gallery(ref e) => self.run_vec(&e.content, settings, out)?,
            Element::Heading(ref e) => {
                self.run_vec(&e.caption, settings, out)?;
                self.run_vec(&e.content, settings, out)?;
            }
            Element::Template(ref e) => {
                self.run_vec(&e.name, settings, out)?;
                self.run_vec(&e.content, settings, out)?;
            }
            Element::TemplateArgument(ref e) => self.run_vec(&e.value, settings, out)?,
            Element::InternalReference(ref e) => {
                self.run_vec(&e.target, settings, out)?;
                for option in &e.options {
                    self.run_vec(option, settings, out)?;
                }
                self.run_vec(&e.caption, settings, out)?;
            }
            Element::ExternalReference(ref e) => self.run_vec(&e.caption, settings, out)?,
            Element::Table(ref e) => {
                self.run_vec(&e.caption, settings, out)?;
                self.run_vec(&e.rows, settings, out)?;
            }
            Element::TableRow(ref e) => self.run_vec(&e.cells, settings, out)?,
            Element::Text(_) | Element::Comment(_) | Element::Error(_) => (),
        }
        self.path_pop();
        Ok(())
    }
}