1use crate::Bytes;
2use crate::InnerNodeHandle;
3#[cfg(feature = "std")]
4use crate::ParserOptions;
5use crate::errors::ParseError;
6#[cfg(feature = "std")]
7use crate::inline::vec::InlineVecIter;
8use crate::parser::HTMLVersion;
9use crate::parser::NodeHandle;
10use crate::queryselector;
11use crate::queryselector::QuerySelectorIterator;
12use crate::{Node, Parser};
13use core::fmt;
14#[cfg(feature = "std")]
15use core::marker::PhantomData;
16
17#[derive(Debug)]
23pub struct VDom<
24 'a,
25 const MAX_NODES: usize = 0,
26 const MAX_STACK: usize = 0,
27 const MAX_ROOTS: usize = 0,
28 const MAX_IDS: usize = 0,
29 const MAX_CLASSES: usize = 0,
30 const MAX_SELECTOR_NODES: usize = 0,
31> {
32 parser: Parser<'a, MAX_NODES, MAX_STACK, MAX_ROOTS, MAX_IDS, MAX_CLASSES, MAX_SELECTOR_NODES>,
34}
35
36impl<
37 'a,
38 const MAX_NODES: usize,
39 const MAX_STACK: usize,
40 const MAX_ROOTS: usize,
41 const MAX_IDS: usize,
42 const MAX_CLASSES: usize,
43 const MAX_SELECTOR_NODES: usize,
44> From<Parser<'a, MAX_NODES, MAX_STACK, MAX_ROOTS, MAX_IDS, MAX_CLASSES, MAX_SELECTOR_NODES>>
45 for VDom<'a, MAX_NODES, MAX_STACK, MAX_ROOTS, MAX_IDS, MAX_CLASSES, MAX_SELECTOR_NODES>
46{
47 fn from(
48 parser: Parser<
49 'a,
50 MAX_NODES,
51 MAX_STACK,
52 MAX_ROOTS,
53 MAX_IDS,
54 MAX_CLASSES,
55 MAX_SELECTOR_NODES,
56 >,
57 ) -> Self {
58 Self { parser }
59 }
60}
61
62impl<
63 'a,
64 const MAX_NODES: usize,
65 const MAX_STACK: usize,
66 const MAX_ROOTS: usize,
67 const MAX_IDS: usize,
68 const MAX_CLASSES: usize,
69 const MAX_SELECTOR_NODES: usize,
70> VDom<'a, MAX_NODES, MAX_STACK, MAX_ROOTS, MAX_IDS, MAX_CLASSES, MAX_SELECTOR_NODES>
71{
72 #[inline]
74 pub fn parser(
75 &self,
76 ) -> &Parser<'a, MAX_NODES, MAX_STACK, MAX_ROOTS, MAX_IDS, MAX_CLASSES, MAX_SELECTOR_NODES>
77 {
78 &self.parser
79 }
80
81 #[inline]
83 pub fn parser_mut(
84 &mut self,
85 ) -> &mut Parser<'a, MAX_NODES, MAX_STACK, MAX_ROOTS, MAX_IDS, MAX_CLASSES, MAX_SELECTOR_NODES>
86 {
87 &mut self.parser
88 }
89
90 pub fn get_element_by_id<'b, S>(&'b self, id: S) -> Option<NodeHandle>
92 where
93 S: Into<Bytes<'a>>,
94 {
95 let bytes: Bytes = id.into();
96 let parser = self.parser();
97
98 if parser.options.is_tracking_ids() {
99 parser.ids.get(&bytes).copied()
100 } else {
101 self.nodes()
102 .iter()
103 .enumerate()
104 .find(|(_, node)| {
105 node.as_tag().is_some_and(|tag| {
106 tag._attributes.id.as_ref().is_some_and(|x| x.eq(&bytes))
107 })
108 })
109 .map(|(id, _)| NodeHandle::new(id as InnerNodeHandle))
110 }
111 }
112
113 pub fn nodes(&self) -> &[Node<'a>] {
121 self.parser.tags.as_slice()
122 }
123
124 pub fn nodes_mut(&mut self) -> &mut [Node<'a>] {
129 self.parser.tags.as_mut_slice()
130 }
131
132 pub fn children(&self) -> &[NodeHandle] {
134 self.parser.ast.as_slice()
135 }
136
137 pub fn children_mut(&mut self) -> &mut [NodeHandle] {
139 self.parser.ast.as_mut_slice()
140 }
141
142 pub fn version(&self) -> Option<HTMLVersion> {
145 self.parser.version
146 }
147
148 pub fn write_outer_html<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
150 for handle in self.children() {
151 if let Some(node) = handle.get(&self.parser) {
152 node.write_outer_html(&self.parser, dest)?;
153 }
154 }
155
156 Ok(())
157 }
158
159 #[cfg(not(feature = "std"))]
161 pub fn query_selector<'b>(
162 &'b self,
163 selector: &'b str,
164 ) -> Result<
165 QuerySelectorIterator<
166 'a,
167 'b,
168 Self,
169 MAX_NODES,
170 MAX_STACK,
171 MAX_ROOTS,
172 MAX_IDS,
173 MAX_CLASSES,
174 MAX_SELECTOR_NODES,
175 >,
176 ParseError,
177 > {
178 let selector = crate::parse_query_selector::<MAX_SELECTOR_NODES>(selector)?;
179 Ok(queryselector::QuerySelectorIterator::new(
180 selector,
181 self.parser(),
182 self,
183 ))
184 }
185}
186
187#[cfg(feature = "std")]
188impl<
189 'a,
190 const MAX_NODES: usize,
191 const MAX_STACK: usize,
192 const MAX_ROOTS: usize,
193 const MAX_IDS: usize,
194 const MAX_CLASSES: usize,
195> VDom<'a, MAX_NODES, MAX_STACK, MAX_ROOTS, MAX_IDS, MAX_CLASSES, 0>
196{
197 pub fn get_elements_by_class_name<'b>(
199 &'b self,
200 id: &'b str,
201 ) -> ClassNameIterator<'a, 'b, MAX_NODES> {
202 let parser = self.parser();
203
204 if parser.options.is_tracking_classes() {
205 parser
206 .classes
207 .get(&Bytes::from(id.as_bytes()))
208 .map(|handles| ClassNameIterator::Tracked(handles.iter()))
209 .unwrap_or_else(|| ClassNameIterator::Empty)
210 } else {
211 ClassNameIterator::Scan {
212 member: id,
213 iter: self.nodes().iter().enumerate(),
214 }
215 }
216 }
217
218 pub fn outer_html(&self) -> String {
220 let mut inner_html = String::with_capacity(self.parser.stream.len());
221
222 for node in self.children() {
223 let node = node.get(&self.parser).unwrap();
224 let _ = node.write_outer_html(&self.parser, &mut inner_html);
225 }
226
227 inner_html
228 }
229
230 pub fn query_selector<'b>(
232 &'b self,
233 selector: &'b str,
234 ) -> Option<
235 QuerySelectorIterator<
236 'a,
237 'b,
238 Self,
239 MAX_NODES,
240 MAX_STACK,
241 MAX_ROOTS,
242 MAX_IDS,
243 MAX_CLASSES,
244 0,
245 >,
246 > {
247 let selector = crate::parse_query_selector(selector)?;
248 let iter = queryselector::QuerySelectorIterator::new(selector, self.parser(), self);
249 Some(iter)
250 }
251}
252
253#[cfg(feature = "std")]
255pub enum ClassNameIterator<'a, 'b, const MAX_NODES: usize = 0> {
256 Empty,
258 Tracked(InlineVecIter<'b, NodeHandle, MAX_NODES>),
260 Scan {
262 member: &'b str,
263 iter: core::iter::Enumerate<core::slice::Iter<'b, Node<'a>>>,
264 },
265}
266
267#[cfg(feature = "std")]
268impl<'a, 'b, const MAX_NODES: usize> Iterator for ClassNameIterator<'a, 'b, MAX_NODES> {
269 type Item = NodeHandle;
270
271 fn next(&mut self) -> Option<Self::Item> {
272 match self {
273 Self::Empty => None,
274 Self::Tracked(iter) => iter.next().copied(),
275 Self::Scan { member, iter } => iter.find_map(|(id, node)| {
276 node.as_tag().and_then(|tag| {
277 tag._attributes
278 .is_class_member(*member)
279 .then(|| NodeHandle::new(id as InnerNodeHandle))
280 })
281 }),
282 }
283 }
284}
285
286#[derive(Debug)]
291#[cfg(feature = "std")]
292pub struct VDomGuard {
293 dom: VDom<
295 'static,
296 { crate::STD_INLINE_CLASS_HANDLES },
297 0,
298 0,
299 { crate::STD_INLINE_IDS },
300 { crate::STD_INLINE_CLASSES },
301 0,
302 >,
303 _s: RawString,
305 _phantom: PhantomData<&'static str>,
307}
308
309#[cfg(feature = "std")]
310unsafe impl Send for VDomGuard {}
311#[cfg(feature = "std")]
312unsafe impl Sync for VDomGuard {}
313
314#[cfg(feature = "std")]
315impl VDomGuard {
316 pub(crate) fn parse(input: String, options: ParserOptions) -> Result<VDomGuard, ParseError> {
318 let input = RawString::new(input);
319
320 let ptr = input.as_ptr();
321
322 let input_ref: &'static str = unsafe { &*ptr };
323
324 let mut parser = Parser::<
330 { crate::STD_INLINE_CLASS_HANDLES },
331 0,
332 0,
333 { crate::STD_INLINE_IDS },
334 { crate::STD_INLINE_CLASSES },
335 0,
336 >::new(input_ref, options);
337 parser.parse()?;
338
339 Ok(Self {
340 _s: input,
341 dom: VDom::from(parser),
342 _phantom: PhantomData,
343 })
344 }
345}
346
347#[cfg(feature = "std")]
348impl VDomGuard {
349 pub fn get_ref<'a>(
353 &'a self,
354 ) -> &'a VDom<
355 'a,
356 { crate::STD_INLINE_CLASS_HANDLES },
357 0,
358 0,
359 { crate::STD_INLINE_IDS },
360 { crate::STD_INLINE_CLASSES },
361 0,
362 > {
363 &self.dom
364 }
365
366 pub fn get_mut_ref<'a, 'b: 'a>(
370 &'b mut self,
371 ) -> &'b VDom<
372 'a,
373 { crate::STD_INLINE_CLASS_HANDLES },
374 0,
375 0,
376 { crate::STD_INLINE_IDS },
377 { crate::STD_INLINE_CLASSES },
378 0,
379 > {
380 &mut self.dom
381 }
382}
383
384#[derive(Debug)]
385#[cfg(feature = "std")]
386struct RawString(*mut str);
387
388#[cfg(feature = "std")]
389impl RawString {
390 pub fn new(s: String) -> Self {
391 Self(Box::into_raw(s.into_boxed_str()))
392 }
393
394 pub fn as_ptr(&self) -> *mut str {
395 self.0
396 }
397}
398
399#[cfg(feature = "std")]
400impl Drop for RawString {
401 fn drop(&mut self) {
402 unsafe {
404 drop(Box::from_raw(self.0));
405 };
406 }
407}