bracket/parser/
iter.rs

1//! Iterators for the AST nodes.
2use crate::{
3    parser::ast::Node,
4    trim::{TrimHint, TrimState},
5};
6
7/// Event that encapsulates a node and whitespace trim state.
8#[derive(Debug)]
9pub struct NodeEvent<'a> {
10    /// The node being emitted.
11    pub node: &'a Node<'a>,
12    /// The trim state for the node.
13    pub trim: TrimState,
14    /// Whether this is the first event in the current iteration.
15    pub first: bool,
16    /// Whether this is the last event in the current iteration.
17    pub last: bool,
18}
19
20impl<'a> NodeEvent<'a> {
21    /// Create a new node event.
22    pub fn new(
23        node: &'a Node,
24        trim: TrimState,
25        first: bool,
26        last: bool,
27    ) -> Self {
28        Self {
29            node,
30            trim,
31            first,
32            last,
33        }
34    }
35}
36
37/// Iterator for branch nodes.
38///
39/// Descends into document and block nodes and yields the child
40/// nodes.
41pub struct BranchIter<'source> {
42    node: &'source Node<'source>,
43    children: Option<std::slice::Iter<'source, Node<'source>>>,
44}
45
46impl<'source> BranchIter<'source> {
47    /// Create a new branch iterator.
48    pub fn new(node: &'source Node) -> Self {
49        Self {
50            node,
51            children: None,
52        }
53    }
54
55    /// Create an iterator that adds trim state information
56    /// to each node.
57    ///
58    /// The input hint will be used to determine the trim state
59    /// of the first node.
60    pub fn event(self, hint: Option<TrimHint>) -> EventIter<'source> {
61        EventIter::new(self, hint)
62    }
63}
64
65impl<'source> Iterator for BranchIter<'source> {
66    type Item = &'source Node<'source>;
67
68    fn next(&mut self) -> Option<Self::Item> {
69        let iter = match *self.node {
70            Node::Document(ref node) => {
71                Some(self.children.get_or_insert(node.nodes().iter()))
72            }
73            Node::Block(ref node) => {
74                Some(self.children.get_or_insert(node.nodes().iter()))
75            }
76            Node::Text(_)
77            | Node::Link(_)
78            | Node::Statement(_)
79            | Node::RawStatement(_)
80            | Node::RawComment(_)
81            | Node::Comment(_) => None,
82        };
83
84        if let Some(it) = iter {
85            let child = it.next();
86            if child.is_none() {
87                self.children.take();
88            }
89            child
90        } else {
91            None
92        }
93    }
94}
95
96/// Iterator that yields node events.
97///
98/// Node events contain the underlying node and a trim state that indicates
99/// whether the current node should have leading and trailing
100/// whitespace removed.
101///
102/// They may also be seeded with a [TrimHint](crate::trim::TrimHint) from a
103/// previous iteration.
104pub struct EventIter<'source> {
105    iter: std::iter::Peekable<BranchIter<'source>>,
106    prev_trim_after: Option<bool>,
107    hint: Option<TrimHint>,
108}
109
110impl<'source> EventIter<'source> {
111    /// Create a new event iterator.
112    pub(crate) fn new(
113        nodes: BranchIter<'source>,
114        hint: Option<TrimHint>,
115    ) -> Self {
116        let iter = nodes.peekable();
117        Self {
118            iter,
119            hint,
120            prev_trim_after: None,
121        }
122    }
123}
124
125impl<'source> Iterator for EventIter<'source> {
126    type Item = NodeEvent<'source>;
127
128    fn next(&mut self) -> Option<Self::Item> {
129        let node = self.iter.next();
130        let peek = self.iter.peek();
131
132        let first = self.prev_trim_after.is_none();
133
134        // Trim the start of the current node.
135        let start = if let Some(trim_after) = self.prev_trim_after.take() {
136            //println!("Iterator setting start from prev trim after");
137            trim_after
138        } else {
139            if let Some(hint) = self.hint.take() {
140                hint.after
141            } else {
142                false
143            }
144        };
145
146        // Trim the end of the current node.
147        let mut end = false;
148        if let Some(next) = peek {
149            if next.trim().before {
150                end = true;
151            }
152        }
153
154        if let Some(ref current) = node {
155            self.prev_trim_after = Some(current.trim().after);
156
157            // NOTE: block nodes will determine the trim based
158            // NOTE: on a close tag so we need to clear any
159            // NOTE: previous trim state here
160            let should_clear_trim = match current {
161                Node::Block(_) => true,
162                _ => false,
163            };
164
165            if should_clear_trim {
166                self.prev_trim_after = None;
167            }
168        }
169
170        let state = TrimState::from((start, end));
171
172        node.map(|n| NodeEvent::new(n, state, first, peek.is_none()))
173    }
174}