Skip to main content

tl/queryselector/
iter.rs

1use core::marker::PhantomData;
2
3use crate::{NodeHandle, Parser};
4
5use super::{Selector, iterable::QueryIterable};
6
7/// A query selector iterator that yields matching HTML nodes
8pub struct QuerySelectorIterator<
9    'a,
10    'b,
11    Q: QueryIterable<'a, MAX_NODES, MAX_STACK, MAX_ROOTS, MAX_IDS, MAX_CLASSES, MAX_SELECTOR_NODES>,
12    const MAX_NODES: usize = 0,
13    const MAX_STACK: usize = 0,
14    const MAX_ROOTS: usize = 0,
15    const MAX_IDS: usize = 0,
16    const MAX_CLASSES: usize = 0,
17    const MAX_SELECTOR_NODES: usize = 0,
18> {
19    selector: Selector<'b, MAX_SELECTOR_NODES>,
20    collection: &'b Q,
21    parser:
22        &'b Parser<'a, MAX_NODES, MAX_STACK, MAX_ROOTS, MAX_IDS, MAX_CLASSES, MAX_SELECTOR_NODES>,
23    #[cfg(feature = "std")]
24    context: Option<super::selector::Context>,
25    index: usize,
26    len: usize,
27    _a: PhantomData<&'a ()>,
28}
29
30impl<
31    'a,
32    'b,
33    Q: QueryIterable<'a, MAX_NODES, MAX_STACK, MAX_ROOTS, MAX_IDS, MAX_CLASSES, MAX_SELECTOR_NODES>,
34    const MAX_NODES: usize,
35    const MAX_STACK: usize,
36    const MAX_ROOTS: usize,
37    const MAX_IDS: usize,
38    const MAX_CLASSES: usize,
39    const MAX_SELECTOR_NODES: usize,
40> Clone
41    for QuerySelectorIterator<
42        'a,
43        'b,
44        Q,
45        MAX_NODES,
46        MAX_STACK,
47        MAX_ROOTS,
48        MAX_IDS,
49        MAX_CLASSES,
50        MAX_SELECTOR_NODES,
51    >
52{
53    fn clone(&self) -> Self {
54        Self {
55            selector: self.selector.clone(),
56            #[cfg(feature = "std")]
57            context: self.context.clone(),
58            collection: self.collection,
59            parser: self.parser,
60            index: self.index,
61            len: self.len,
62            _a: PhantomData,
63        }
64    }
65}
66
67impl<
68    'a,
69    'b,
70    Q: QueryIterable<'a, MAX_NODES, MAX_STACK, MAX_ROOTS, MAX_IDS, MAX_CLASSES, MAX_SELECTOR_NODES>,
71    const MAX_NODES: usize,
72    const MAX_STACK: usize,
73    const MAX_ROOTS: usize,
74    const MAX_IDS: usize,
75    const MAX_CLASSES: usize,
76    const MAX_SELECTOR_NODES: usize,
77>
78    QuerySelectorIterator<
79        'a,
80        'b,
81        Q,
82        MAX_NODES,
83        MAX_STACK,
84        MAX_ROOTS,
85        MAX_IDS,
86        MAX_CLASSES,
87        MAX_SELECTOR_NODES,
88    >
89{
90    /// Creates a new query selector iterator
91    pub fn new(
92        selector: Selector<'b, MAX_SELECTOR_NODES>,
93        parser: &'b Parser<
94            'a,
95            MAX_NODES,
96            MAX_STACK,
97            MAX_ROOTS,
98            MAX_IDS,
99            MAX_CLASSES,
100            MAX_SELECTOR_NODES,
101        >,
102        collection: &'b Q,
103    ) -> Self {
104        Self {
105            #[cfg(feature = "std")]
106            context: selector
107                .needs_context()
108                .then(|| super::selector::Context::new(parser.tags.as_slice())),
109            selector,
110            collection,
111            index: 0,
112            len: collection.len(parser),
113            parser,
114            _a: PhantomData,
115        }
116    }
117}
118
119impl<
120    'a,
121    'b,
122    Q: QueryIterable<'a, MAX_NODES, MAX_STACK, MAX_ROOTS, MAX_IDS, MAX_CLASSES, MAX_SELECTOR_NODES>,
123    const MAX_NODES: usize,
124    const MAX_STACK: usize,
125    const MAX_ROOTS: usize,
126    const MAX_IDS: usize,
127    const MAX_CLASSES: usize,
128    const MAX_SELECTOR_NODES: usize,
129> Iterator
130    for QuerySelectorIterator<
131        'a,
132        'b,
133        Q,
134        MAX_NODES,
135        MAX_STACK,
136        MAX_ROOTS,
137        MAX_IDS,
138        MAX_CLASSES,
139        MAX_SELECTOR_NODES,
140    >
141{
142    type Item = NodeHandle;
143
144    fn next(&mut self) -> Option<Self::Item> {
145        while self.index < self.len {
146            let node = self.collection.get(self.parser, self.index);
147            self.index += 1;
148            if let Some((node, id)) = node {
149                #[cfg(feature = "std")]
150                let matches = self.context.as_ref().map_or_else(
151                    || self.selector.matches(node),
152                    |context| {
153                        self.selector.matches_in(
154                            id.get_inner() as usize,
155                            self.parser.tags.as_slice(),
156                            context,
157                        )
158                    },
159                );
160                #[cfg(not(feature = "std"))]
161                let matches = self.selector.matches(node);
162
163                if matches {
164                    return Some(id);
165                }
166            }
167        }
168
169        None
170    }
171}