use crate::errors::ParseError;
use crate::parser::HTMLVersion;
use crate::parser::NodeHandle;
use crate::queryselector;
use crate::queryselector::QuerySelectorIterator;
use crate::Bytes;
use crate::InnerNodeHandle;
use crate::ParserOptions;
use crate::{Node, Parser};
use std::marker::PhantomData;
#[derive(Debug)]
pub struct VDom<'a> {
parser: Parser<'a>,
}
impl<'a> From<Parser<'a>> for VDom<'a> {
fn from(parser: Parser<'a>) -> Self {
Self { parser }
}
}
impl<'a> VDom<'a> {
#[inline]
pub fn parser(&self) -> &Parser<'a> {
&self.parser
}
#[inline]
pub fn parser_mut(&mut self) -> &mut Parser<'a> {
&mut self.parser
}
pub fn get_element_by_id<'b, S>(&'b self, id: S) -> Option<NodeHandle>
where
S: Into<Bytes<'a>>,
{
let bytes: Bytes = id.into();
let parser = self.parser();
if parser.options.is_tracking_ids() {
parser.ids.get(&bytes).copied()
} else {
self.nodes()
.iter()
.enumerate()
.find(|(_, node)| {
node.as_tag().map_or(false, |tag| {
tag._attributes.id.as_ref().map_or(false, |x| x.eq(&bytes))
})
})
.map(|(id, _)| NodeHandle::new(id as InnerNodeHandle))
}
}
pub fn get_elements_by_class_name<'b>(
&'b self,
id: &'b str,
) -> Box<dyn Iterator<Item = NodeHandle> + 'b> {
let parser = self.parser();
if parser.options.is_tracking_classes() {
parser
.classes
.get(&Bytes::from(id.as_bytes()))
.map(|x| Box::new(x.iter().cloned()) as Box<dyn Iterator<Item = NodeHandle>>)
.unwrap_or_else(|| Box::new(std::iter::empty()))
} else {
let member = id;
let iter = self
.nodes()
.iter()
.enumerate()
.filter_map(move |(id, node)| {
node.as_tag().and_then(|tag| {
tag._attributes
.is_class_member(member)
.then(|| NodeHandle::new(id as InnerNodeHandle))
})
});
Box::new(iter)
}
}
pub fn nodes(&self) -> &[Node<'a>] {
&self.parser.tags
}
pub fn nodes_mut(&mut self) -> &mut [Node<'a>] {
&mut self.parser.tags
}
pub fn children(&self) -> &[NodeHandle] {
&self.parser.ast
}
pub fn children_mut(&mut self) -> &mut [NodeHandle] {
&mut self.parser.ast
}
pub fn version(&self) -> Option<HTMLVersion> {
self.parser.version
}
pub fn outer_html(&self) -> String {
let mut inner_html = String::with_capacity(self.parser.stream.len());
for node in self.children() {
let node = node.get(&self.parser).unwrap();
inner_html.push_str(&node.outer_html(&self.parser));
}
inner_html
}
pub fn query_selector<'b>(
&'b self,
selector: &'b str,
) -> Option<QuerySelectorIterator<'a, 'b, Self>> {
let selector = crate::parse_query_selector(selector)?;
let iter = queryselector::QuerySelectorIterator::new(selector, self.parser(), self);
Some(iter)
}
}
#[derive(Debug)]
pub struct VDomGuard {
dom: VDom<'static>,
_s: RawString,
_phantom: PhantomData<&'static str>,
}
unsafe impl Send for VDomGuard {}
unsafe impl Sync for VDomGuard {}
impl VDomGuard {
pub(crate) fn parse(input: String, options: ParserOptions) -> Result<VDomGuard, ParseError> {
let input = RawString::new(input);
let ptr = input.as_ptr();
let input_ref: &'static str = unsafe { &*ptr };
let mut parser = Parser::new(input_ref, options);
parser.parse()?;
Ok(Self {
_s: input,
dom: VDom::from(parser),
_phantom: PhantomData,
})
}
}
impl VDomGuard {
pub fn get_ref<'a>(&'a self) -> &'a VDom<'a> {
&self.dom
}
pub fn get_mut_ref<'a, 'b: 'a>(&'b mut self) -> &'b VDom<'a> {
&mut self.dom
}
}
#[derive(Debug)]
struct RawString(*mut str);
impl RawString {
pub fn new(s: String) -> Self {
Self(Box::into_raw(s.into_boxed_str()))
}
pub fn as_ptr(&self) -> *mut str {
self.0
}
}
impl Drop for RawString {
fn drop(&mut self) {
unsafe {
drop(Box::from_raw(self.0));
};
}
}