use vize_l0::{Span, Vec};
use super::{DocumentLexicalRefusal, DocumentTreePolicy, NativeDocument};
mod attribute;
mod build;
mod node;
pub use attribute::{DocumentHtmlAttribute, DocumentHtmlAttributes};
pub use node::{DocumentHtmlChildNodes, DocumentHtmlComment, DocumentHtmlNode, DocumentHtmlText};
#[derive(Debug)]
struct Element {
name: &'static str,
authored_name: Span,
opening: Span,
closing: Option<Span>,
parent: Option<usize>,
next_sibling: Option<usize>,
first_child: Option<usize>,
last_child: Option<usize>,
ignored_slash: bool,
attributes: core::ops::Range<usize>,
content_end: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DocumentHtmlRefusal {
Lexical(DocumentLexicalRefusal),
ExplicitEnvelope,
UnsupportedElement(Span),
UnsupportedPolicy(DocumentTreePolicy),
VueSyntax,
NonHtmlToken,
InvalidFrame(Span),
ImpliedEnd(Span),
PendingStructure,
AttributeCountLimit(Span),
OpeningByteLimit(Span),
UnsupportedNodeParent(Span),
OutsideBodyText(Span),
}
#[derive(Debug)]
pub struct DocumentHtmlStructure<'o, 'a> {
owner: &'o NativeDocument<'a>,
elements: Vec<'a, Element>,
body_tail_text: Option<Span>,
}
impl<'o, 'a> DocumentHtmlStructure<'o, 'a> {
pub const MAX_ATTRIBUTES: usize = 64;
pub const MAX_OPENING_BYTES: u32 = 16 * 1024;
#[must_use]
pub fn owner(&self) -> &'o NativeDocument<'a> {
self.owner
}
#[must_use]
pub fn elements(&self) -> impl ExactSizeIterator<Item = DocumentHtmlElement<'_, 'o, 'a>> {
self.elements.iter().map(|element| DocumentHtmlElement {
tree: self,
element,
})
}
}
#[derive(Debug)]
pub struct DocumentHtmlElement<'s, 'o, 'a> {
tree: &'s DocumentHtmlStructure<'o, 'a>,
element: &'s Element,
}
impl<'s, 'o, 'a> DocumentHtmlElement<'s, 'o, 'a> {
fn element(&self) -> &Element {
self.element
}
#[must_use]
pub fn owner(&self) -> &'o NativeDocument<'a> {
self.tree.owner
}
#[must_use]
pub fn name(&self) -> &'static str {
self.element().name
}
#[must_use]
pub fn authored_name(&self) -> &'a str {
self.element().authored_name.slice(self.owner().source())
}
#[must_use]
pub fn opening_span(&self) -> Span {
self.element().opening
}
#[must_use]
pub fn opening(&self) -> &'a str {
self.opening_span().slice(self.owner().source())
}
#[must_use]
pub fn closing_span(&self) -> Option<Span> {
self.element().closing
}
#[must_use]
pub fn ignored_self_closing_slash(&self) -> bool {
self.element().ignored_slash
}
#[must_use]
pub fn attributes(&self) -> DocumentHtmlAttributes<'s, 'o, 'a> {
attribute::attributes(self.tree, self.element)
}
pub fn child_nodes(&self) -> Result<DocumentHtmlChildNodes<'s, 'o, 'a>, DocumentHtmlRefusal> {
node::children(self.tree, self.element)
}
#[must_use]
pub fn parent(&self) -> Option<Self> {
self.element()
.parent
.and_then(|index| self.tree.elements.get(index))
.map(|element| Self {
tree: self.tree,
element,
})
}
pub fn children(&self) -> impl Iterator<Item = Self> + 's {
let tree = self.tree;
let mut next = self.element().first_child;
core::iter::from_fn(move || {
let index = next?;
let element = tree.elements.get(index)?;
next = element.next_sibling;
Some(Self { tree, element })
})
}
}
pub(super) fn construct<'o, 'a>(
owner: &'o NativeDocument<'a>,
) -> Result<DocumentHtmlStructure<'o, 'a>, DocumentHtmlRefusal> {
build::construct(owner)
}
#[cfg(test)]
mod tests;