use std::borrow::Cow;
use crate::Tag;
pub(crate) const NONE: u32 = u32::MAX;
const OWNED: u32 = 1 << 31;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(crate) struct Span {
start: u32,
len: u32,
}
impl Span {
pub(crate) fn source(start: usize, end: usize) -> Span {
Span {
start: start as u32,
len: (end - start) as u32,
}
}
pub(crate) fn owned(start: usize, end: usize) -> Span {
Span {
start: start as u32 | OWNED,
len: (end - start) as u32,
}
}
pub(crate) fn is_owned(self) -> bool {
self.start & OWNED != 0
}
pub(crate) fn offset(self) -> usize {
(self.start & !OWNED) as usize
}
pub(crate) fn end(self) -> usize {
self.offset() + self.len as usize
}
pub(crate) fn is_empty(self) -> bool {
self.len == 0
}
pub(crate) fn skip(self, n: usize) -> Span {
Span {
start: self.start + n as u32,
len: self.len - n as u32,
}
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct Attr {
pub(crate) name: Span,
pub(crate) value: Span,
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum Data {
Document,
Doctype,
Element {
tag: Tag,
name: Span,
attrs: u32,
attrs_len: u32,
},
Text(Span),
Comment(Span),
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct Node {
pub(crate) parent: u32,
pub(crate) first_child: u32,
pub(crate) last_child: u32,
pub(crate) prev: u32,
pub(crate) next: u32,
pub(crate) data: Data,
}
impl Node {
pub(crate) fn new(data: Data) -> Node {
Node {
parent: NONE,
first_child: NONE,
last_child: NONE,
prev: NONE,
next: NONE,
data,
}
}
}
pub struct Html<'s> {
pub(crate) src: Cow<'s, str>,
pub(crate) buf: String,
pub(crate) nodes: Vec<Node>,
pub(crate) attrs: Vec<Attr>,
}
impl<'s> Html<'s> {
pub fn parse(html: &'s str) -> Html<'s> {
crate::builder::parse(html)
}
pub fn root(&self) -> NodeRef<'_> {
NodeRef { doc: self, id: 0 }
}
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.nodes.len() <= 1
}
pub fn get(&self, id: NodeId) -> NodeRef<'_> {
NodeRef {
doc: self,
id: id.0,
}
}
pub fn body(&self) -> NodeRef<'_> {
self.root()
.descendants()
.find(|n| n.tag() == Some(Tag::Body))
.unwrap_or_else(|| self.root())
}
pub(crate) fn str(&self, s: Span) -> &str {
let (start, end) = (s.offset(), s.end());
if s.is_owned() {
&self.buf[start..end]
} else {
&self.src[start..end]
}
}
}
impl std::fmt::Debug for Html<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Html")
.field("nodes", &self.nodes.len())
.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct NodeId(u32);
impl NodeId {
pub fn index(self) -> usize {
self.0 as usize
}
}
#[derive(Clone, Copy)]
pub struct NodeRef<'a> {
doc: &'a Html<'a>,
id: u32,
}
impl PartialEq for NodeRef<'_> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id && std::ptr::eq(self.doc, other.doc)
}
}
impl Eq for NodeRef<'_> {}
impl std::fmt::Debug for NodeRef<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.node().data {
Data::Document => write!(f, "#document"),
Data::Doctype => write!(f, "<!DOCTYPE>"),
Data::Element { .. } => write!(f, "<{}>", self.as_element().unwrap().name()),
Data::Text(s) => write!(f, "{:?}", self.doc.str(s)),
Data::Comment(s) => write!(f, "<!--{}-->", self.doc.str(s)),
}
}
}
impl<'a> NodeRef<'a> {
fn node(&self) -> &'a Node {
&self.doc.nodes[self.id as usize]
}
fn at(&self, id: u32) -> Option<NodeRef<'a>> {
(id != NONE).then_some(NodeRef { doc: self.doc, id })
}
pub fn id(&self) -> NodeId {
NodeId(self.id)
}
pub fn document(&self) -> &'a Html<'a> {
self.doc
}
pub fn parent(&self) -> Option<NodeRef<'a>> {
self.at(self.node().parent)
}
pub fn first_child(&self) -> Option<NodeRef<'a>> {
self.at(self.node().first_child)
}
pub fn last_child(&self) -> Option<NodeRef<'a>> {
self.at(self.node().last_child)
}
pub fn next_sibling(&self) -> Option<NodeRef<'a>> {
self.at(self.node().next)
}
pub fn prev_sibling(&self) -> Option<NodeRef<'a>> {
self.at(self.node().prev)
}
pub fn children(&self) -> Children<'a> {
Children {
doc: self.doc,
front: self.node().first_child,
back: self.node().last_child,
}
}
pub fn descendants(&self) -> Descendants<'a> {
Descendants {
doc: self.doc,
root: self.id,
next: self.id,
}
}
pub fn ancestors(&self) -> Ancestors<'a> {
Ancestors {
doc: self.doc,
next: self.node().parent,
}
}
pub fn is_element(&self) -> bool {
matches!(self.node().data, Data::Element { .. })
}
pub fn is_text(&self) -> bool {
matches!(self.node().data, Data::Text(_))
}
pub fn is_comment(&self) -> bool {
matches!(self.node().data, Data::Comment(_))
}
pub fn is_document(&self) -> bool {
matches!(self.node().data, Data::Document)
}
pub fn as_element(&self) -> Option<Element<'a>> {
match self.node().data {
Data::Element {
tag,
name,
attrs,
attrs_len,
} => Some(Element {
doc: self.doc,
tag,
name,
attrs: &self.doc.attrs[attrs as usize..(attrs + attrs_len) as usize],
}),
_ => None,
}
}
pub fn tag(&self) -> Option<Tag> {
match self.node().data {
Data::Element { tag, .. } => Some(tag),
_ => None,
}
}
pub fn as_text(&self) -> Option<&'a str> {
match self.node().data {
Data::Text(s) => Some(self.doc.str(s)),
_ => None,
}
}
pub fn as_comment(&self) -> Option<&'a str> {
match self.node().data {
Data::Comment(s) => Some(self.doc.str(s)),
_ => None,
}
}
pub fn text(&self) -> String {
let mut out = String::new();
for d in self.descendants() {
if let Some(t) = d.as_text() {
out.push_str(t);
}
}
out
}
}
#[derive(Clone, Copy)]
pub struct Element<'a> {
doc: &'a Html<'a>,
tag: Tag,
name: Span,
attrs: &'a [Attr],
}
impl<'a> Element<'a> {
pub fn tag(&self) -> Tag {
self.tag
}
pub fn name(&self) -> &'a str {
match self.tag {
Tag::Other => self.doc.str(self.name),
t => t.name(),
}
}
pub fn attr(&self, name: &str) -> Option<&'a str> {
self.attrs
.iter()
.find(|a| a.name.len as usize == name.len() && self.doc.str(a.name) == name)
.map(|a| self.doc.str(a.value))
}
pub fn attrs(&self) -> impl Iterator<Item = (&'a str, &'a str)> + 'a {
let doc = self.doc;
self.attrs
.iter()
.map(move |a| (doc.str(a.name), doc.str(a.value)))
}
}
impl std::fmt::Debug for Element<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<{}", self.name())?;
for (k, v) in self.attrs() {
write!(f, " {k}={v:?}")?;
}
write!(f, ">")
}
}
pub struct Children<'a> {
doc: &'a Html<'a>,
front: u32,
back: u32,
}
impl<'a> Iterator for Children<'a> {
type Item = NodeRef<'a>;
fn next(&mut self) -> Option<NodeRef<'a>> {
if self.front == NONE {
return None;
}
let id = self.front;
if id == self.back {
self.front = NONE;
self.back = NONE;
} else {
self.front = self.doc.nodes[id as usize].next;
}
Some(NodeRef { doc: self.doc, id })
}
}
impl DoubleEndedIterator for Children<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.back == NONE {
return None;
}
let id = self.back;
if id == self.front {
self.front = NONE;
self.back = NONE;
} else {
self.back = self.doc.nodes[id as usize].prev;
}
Some(NodeRef { doc: self.doc, id })
}
}
pub struct Descendants<'a> {
doc: &'a Html<'a>,
root: u32,
next: u32,
}
impl<'a> Iterator for Descendants<'a> {
type Item = NodeRef<'a>;
fn next(&mut self) -> Option<NodeRef<'a>> {
if self.next == NONE {
return None;
}
let id = self.next;
let nodes = &self.doc.nodes;
let n = &nodes[id as usize];
self.next = if n.first_child != NONE {
n.first_child
} else {
let mut cur = id;
loop {
if cur == self.root {
break NONE;
}
let c = &nodes[cur as usize];
if c.next != NONE {
break c.next;
}
cur = c.parent;
if cur == NONE {
break NONE;
}
}
};
Some(NodeRef { doc: self.doc, id })
}
}
pub struct Ancestors<'a> {
doc: &'a Html<'a>,
next: u32,
}
impl<'a> Iterator for Ancestors<'a> {
type Item = NodeRef<'a>;
fn next(&mut self) -> Option<NodeRef<'a>> {
if self.next == NONE {
return None;
}
let id = self.next;
self.next = self.doc.nodes[id as usize].parent;
Some(NodeRef { doc: self.doc, id })
}
}