vize_l1 0.590.0

L1 - the lossless Vue-template surface tree (codename Sinopia, Davinci P2-7)
Documentation
//! Bounded HTML element ancestry from the sole original Document event tape.
//! No Component surface, external event vectors, reparse or DOM payloads enter.

use vize_l0::{Span, Vec};

use super::{DocumentLexicalRefusal, 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,
}

/// Why no bounded native element structure was produced for the original run.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DocumentHtmlRefusal {
    Lexical(DocumentLexicalRefusal),
    ExplicitEnvelope,
    UnsupportedElement(Span),
    VueSyntax,
    NonHtmlToken,
    InvalidFrame(Span),
    /// An end the browser would imply with a parse error, or ignore.
    ImpliedEnd(Span),
    /// A table part whose parent the browser would insert without a tag
    /// (`tbody` for `tr`, `tr` for a cell, `colgroup` for `col`).
    ImpliedStart(Span),
    /// A tag the browser ignores, such as a table part outside a table.
    IgnoredTag(Span),
    /// Content the browser would move in front of its table.
    FosterParenting(Span),
    /// Non-whitespace text directly in a table, row group, row or column group.
    TableText(Span),
    PendingStructure,
    AttributeCountLimit(Span),
    OpeningByteLimit(Span),
    UnsupportedNodeParent(Span),
    OutsideBodyText(Span),
}

/// Actual element ancestry for a strict explicit no-quirks HTML envelope.
///
/// Supported envelope: exactly `<!DOCTYPE html>`, `html`, `head`, then `body`,
/// with explicit ends. Head children are `base`, `link` and `meta`; body
/// elements are `div`, `span`, `br`, `hr`, `img`, `input`, `area`, `embed`,
/// `source`, `track`, `wbr`, `article`, `aside`, `footer`, `header`, `main`, `nav`
/// `section`, `address`, `blockquote`, `figure`, `figcaption`, `hgroup`,
/// `search`, `p`, the `ul`/`ol`/`menu`/`dl` lists and their items, ruby text
/// (`ruby`, `rb`, `rt`, `rtc`, `rp`), `option`/`optgroup` outside `select`,
/// and table parts. HTML names match without ASCII case, and a slash never
/// closes a nonvoid HTML element.
///
/// Implied end tags and the table insertion modes follow the HTML parser
/// when they report no parse error, so an omitted `</p>`, `</li>` or `</td>`
/// closes its element without a closing span. Implied starts, fostered
/// content, ignored tags and parse-error ends refuse with typed reasons.
/// Static attributes borrow their original callback ranges, with HTML ASCII
/// name folding, first duplicate retention and once-decoded scalar values.
/// Every authored start tag has at most 64 attributes (including duplicates)
/// and 16 KiB of original opening bytes. Larger tags explicitly refuse.
///
/// Foreign content, raw-text elements, formatting, `select`, templates, Vue
/// directive/interpolation syntax and implicit envelopes refuse this provider.
/// Direct body-subtree child nodes retain original text/comment callbacks;
/// outer insertion-mode text is a separate typed refusal. Petite-vue remains
/// unfinished. This view borrows the actual original lexer owner and arena.
///
/// ```compile_fail
/// use vize_l1::markup::document::DocumentHtmlStructure;
/// fn requires_clone<T: Clone>() {}
/// requires_clone::<DocumentHtmlStructure<'static, 'static>>();
/// ```
///
/// ```compile_fail
/// use vize_l1::markup::document::DocumentHtmlStructure;
/// fn forge<'o, 'a>(original: DocumentHtmlStructure<'o, 'a>) -> DocumentHtmlStructure<'o, 'a> {
///     DocumentHtmlStructure { ..original }
/// }
/// ```
#[derive(Debug)]
pub struct DocumentHtmlStructure<'o, 'a> {
    owner: &'o NativeDocument<'a>,
    elements: Vec<'a, Element>,
    body_tail_text: Option<Span>,
    /// The current node at `</body>`, which receives later whitespace.
    tail_owner: Option<usize>,
}

impl<'o, 'a> DocumentHtmlStructure<'o, 'a> {
    /// Maximum authored attributes per admitted tag, including duplicates.
    pub const MAX_ATTRIBUTES: usize = 64;
    /// Maximum original complete start-tag byte length for this provider.
    pub const MAX_OPENING_BYTES: u32 = 16 * 1024;

    #[must_use]
    pub fn owner(&self) -> &'o NativeDocument<'a> {
        self.owner
    }

    /// Elements in original start-tag order; each has its actual HTML parent.
    pub fn elements(&self) -> impl ExactSizeIterator<Item = DocumentHtmlElement<'_, 'o, 'a>> {
        self.elements.iter().map(|element| DocumentHtmlElement {
            tree: self,
            element,
        })
    }
}

/// A readonly element borrowing its genuine source-owned structure.
///
/// ```compile_fail
/// use vize_l1::markup::document::DocumentHtmlElement;
/// fn requires_clone<T: Clone>() {}
/// requires_clone::<DocumentHtmlElement<'static, 'static, 'static>>();
/// ```
///
/// ```compile_fail
/// use vize_l1::markup::document::DocumentHtmlElement;
/// fn forge<'s, 'o, 'a>(original: DocumentHtmlElement<'s, 'o, 'a>) -> DocumentHtmlElement<'s, 'o, 'a> {
///     DocumentHtmlElement { ..original }
/// }
/// ```
#[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
    }

    /// The actual HTML local name, folded by native construction.
    #[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())
    }

    /// Void elements and elements closed by an implied end have no closing tag.
    #[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
    }

    /// Effective static HTML attributes, in original first-occurrence order.
    /// Duplicate-name comparisons borrow the earlier original events without
    /// allocating a name set; duplicate filtering is quadratic in tag size.
    #[must_use]
    pub fn attributes(&self) -> DocumentHtmlAttributes<'s, 'o, 'a> {
        attribute::attributes(self.tree, self.element)
    }

    /// Original direct child nodes of body-subtree elements.
    /// Text callbacks coalesce across references, comments retain literal
    /// reference bytes, and child elements use the existing original adjacency.
    /// Head/html insertion modes refuse. Text after the authored `</body>`
    /// also refuses on the element open there (body, or an implied-end
    /// descendant) rather than claiming that source-range custody models the
    /// browser's outside-body insertion rules.
    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,
            })
    }

    /// Direct element children, following the producer's original adjacency.
    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;