bc_envelope/base/
walk.rs

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::Envelope;

use super::envelope::EnvelopeCase;

/// The type of incoming edge provided to the visitor.
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum EdgeType {
    None,
    Subject,
    Assertion,
    Predicate,
    Object,
    Wrapped,
}

impl EdgeType {
    pub fn label(&self) -> Option<&'static str> {
        match self {
            EdgeType::Subject | EdgeType::Wrapped => Some("subj"),
            EdgeType::Predicate => Some("pred"),
            EdgeType::Object => Some("obj"),
            _ => None,
        }
    }
}

/// A visitor function that is called for each node in the envelope.
pub type Visitor<'a, Parent> = dyn Fn(Envelope, usize, EdgeType, Option<Parent>) -> Option<Parent> + 'a;

/// Functions for walking an envelope.
impl Envelope {
    /// Walk the envelope, calling the visitor function for each element.
    ///
    /// If `hide_nodes` is true, then the visitor function will not be called for nodes,
    /// but only for the children of nodes.
    pub fn walk<Parent: Clone>(&self, hide_nodes: bool, visit: &Visitor<'_, Parent>) {
        if hide_nodes {
            self.walk_tree(visit);
        } else {
            self.walk_structure(visit);
        }
    }

    fn walk_structure<Parent: Clone>(&self, visit: &Visitor<'_, Parent>) {
        self._walk_structure(0, EdgeType::None, None, visit);
    }

    fn _walk_structure<Parent: Clone>(&self, level: usize, incoming_edge: EdgeType, parent: Option<Parent>, visit: &Visitor<'_, Parent>) {
        let parent = visit(self.clone(), level, incoming_edge, parent);
        let next_level = level + 1;
        match self.case() {
            EnvelopeCase::Node { subject, assertions, .. } => {
                subject._walk_structure(next_level, EdgeType::Subject, parent.clone(), visit);
                for assertion in assertions {
                    assertion._walk_structure(next_level, EdgeType::Assertion, parent.clone(), visit);
                }
            },
            EnvelopeCase::Wrapped { envelope, .. } => {
                envelope._walk_structure(next_level, EdgeType::Wrapped, parent, visit);
            },
            EnvelopeCase::Assertion(assertion) => {
                assertion.predicate()._walk_structure(next_level, EdgeType::Predicate, parent.clone(), visit);
                assertion.object()._walk_structure(next_level, EdgeType::Object, parent, visit);
            },
            _ => {},
        }
    }

    fn walk_tree<Parent: Clone>(&self, visit: &Visitor<'_, Parent>)
    {
        self._walk_tree(0, None, visit);
    }

    fn _walk_tree<Parent: Clone>(&self, level: usize, parent: Option<Parent>, visit: &Visitor<'_, Parent>) -> Option<Parent> {
        let mut parent = parent;
        let mut subject_level = level;
        if !self.is_node() {
            parent = visit(self.clone(), level, EdgeType::None, parent);
            subject_level = level + 1;
        }
        match self.case() {
            EnvelopeCase::Node { subject, assertions, .. } => {
                let assertion_parent = subject._walk_tree(subject_level, parent.clone(), visit);
                let assertion_level = subject_level + 1;
                for assertion in assertions {
                    assertion._walk_tree(assertion_level, assertion_parent.clone(), visit);
                }
            },
            EnvelopeCase::Wrapped { envelope, .. } => {
                envelope._walk_tree(subject_level, parent.clone(), visit);
            },
            EnvelopeCase::Assertion(assertion) => {
                assertion.predicate()._walk_tree(subject_level, parent.clone(), visit);
                assertion.object()._walk_tree(subject_level, parent.clone(), visit);
            },
            _ => {},
        }
        parent
    }
}