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 fn parse(input: String, options: ParserOptions) -> Result<VDomGuard, ParseError> {
327 let input = RawString::new(input);
328
329 let ptr = input.as_ptr();
330
331 let input_ref: &'static str = unsafe { &*ptr };
337
338 let mut parser = Parser::<
344 { crate::STD_INLINE_CLASS_HANDLES },
345 0,
346 0,
347 { crate::STD_INLINE_IDS },
348 { crate::STD_INLINE_CLASSES },
349 0,
350 >::new(input_ref, options);
351 parser.parse()?;
352
353 Ok(Self {
354 _s: input,
355 dom: VDom::from(parser),
356 _phantom: PhantomData,
357 })
358 }
359}
360
361#[cfg(feature = "std")]
362impl VDomGuard {
363 pub fn get_ref<'a>(
367 &'a self,
368 ) -> &'a VDom<
369 'a,
370 { crate::STD_INLINE_CLASS_HANDLES },
371 0,
372 0,
373 { crate::STD_INLINE_IDS },
374 { crate::STD_INLINE_CLASSES },
375 0,
376 > {
377 &self.dom
378 }
379
380 pub fn get_mut_ref<'a, 'b: 'a>(
385 &'b mut self,
386 ) -> &'b VDom<
387 'a,
388 { crate::STD_INLINE_CLASS_HANDLES },
389 0,
390 0,
391 { crate::STD_INLINE_IDS },
392 { crate::STD_INLINE_CLASSES },
393 0,
394 > {
395 &mut self.dom
396 }
397}
398
399#[derive(Debug)]
400#[cfg(feature = "std")]
401struct RawString(*mut str);
402
403#[cfg(feature = "std")]
404impl RawString {
405 pub fn new(s: String) -> Self {
406 Self(Box::into_raw(s.into_boxed_str()))
407 }
408
409 pub fn as_ptr(&self) -> *mut str {
410 self.0
411 }
412}
413
414#[cfg(feature = "std")]
415impl Drop for RawString {
416 fn drop(&mut self) {
417 unsafe {
419 drop(Box::from_raw(self.0));
420 };
421 }
422}