#@stream

    Event-based HTML parsing. Where @parser builds a full DOM and @tokenizer emits raw tokens, @stream sits in between: it normalizes the token sequence into a small set of high-level events — perfect for serializers, syntax highlighters, link extractors, or any consumer that only needs to see structure pass by.

    The examples below are mbt check blocks and run as part of moon test stream.

    #Event shape

    ///|
    pub(all) enum StreamEvent {
    StreamStart(StreamStartEvent) // open tag with name + attrs
    StreamText(String) // decoded text (entities expanded)
    StreamEnd(String) // close tag with name
    StreamComment(String) // comment data, no delimiters
    StreamDoctype(StreamDoctypeEvent)
    }

    stream returns an array; stream_each invokes a callback per event.

    #A first stream

    Adjacent text is coalesced into a single StreamText.

    ///|
    test "readme stream basic" {
    let events = @stream.stream("<p>Hello <b>World</b></p>")
    debug_inspect(
    events,
    content=(
    #|[
    #| StreamStart({ name: "p", attrs: {} }),
    #| StreamText("Hello "),
    #| StreamStart({ name: "b", attrs: {} }),
    #| StreamText("World"),
    #| StreamEnd("b"),
    #| StreamEnd("p"),
    #|]
    ),
    )
    }

    #Void elements and unmatched closers

    The streamer reports what the input says — it does not synthesize implicit end tags for void elements, and it does not drop unmatched closing tags.

    ///|
    test "readme stream void and unmatched" {
    debug_inspect(
    @stream.stream("<br></div>"),
    content=(
    #|[StreamStart({ name: "br", attrs: {} }), StreamEnd("div")]
    ),
    )
    }

    #Byte input with encoding detection

    stream_bytes runs BOM sniffing and meta-charset prescan when no encoding is provided. The byte 0x80 falls back to windows-1252 (€) when no other signal is present.

    ///|
    test "readme stream_bytes fallback" {
    debug_inspect(
    @stream.stream_bytes(b"<p>\x80</p>"),
    content=(
    #|[
    #| StreamStart({ name: "p", attrs: {} }),
    #| StreamText("€"),
    #| StreamEnd("p"),
    #|]
    ),
    )
    }

    Pass encoding="utf-8" (or any supported label) to skip detection.

    #Incremental delivery

    stream_each is stream without the intermediate array — useful when you want to bail out early or write events straight to a sink.

    ///|
    test "readme stream_each counts text events" {
    let mut text_events = 0
    @stream.stream_each("<p>a<b>b</b>c</p>", event => {
    match event {
    StreamText(_) => text_events = text_events + 1
    _ => ()
    }
    })
    assert_eq(text_events, 3)
    }

    StreamDoctypeEvent

    pub(all) struct StreamDoctypeEvent {
    name : String
    public_id : String?
    system_id : String?
    } derive(Eq,
    Debug
    )

    Doctype event emitted by the streaming tokenizer facade.

    StreamDoctypeEvent::equal

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn StreamDoctypeEvent::equal(StreamDoctypeEvent, StreamDoctypeEvent) -> Bool

    StreamDoctypeEvent::not_equal

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn StreamDoctypeEvent::not_equal(x : StreamDoctypeEvent, y : StreamDoctypeEvent) -> Bool

    StreamDoctypeEvent::to_repr

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn StreamDoctypeEvent::to_repr(StreamDoctypeEvent) ->
    Repr

    StreamEvent

    pub(all) enum StreamEvent {
    StreamStart(StreamStartEvent)
    StreamText(String)
    StreamEnd(String)
    StreamComment(String)
    StreamDoctype(StreamDoctypeEvent)
    } derive(Eq,
    Debug
    )

    Token-level streaming event.

    The stream API does not build a DOM tree. It forwards start tags, end tags, text, comments, and doctypes in tokenizer order, coalescing adjacent text.

    StreamEvent::equal

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn StreamEvent::equal(StreamEvent, StreamEvent) -> Bool

    StreamEvent::not_equal

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn StreamEvent::not_equal(x : StreamEvent, y : StreamEvent) -> Bool

    StreamEvent::to_repr

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn StreamEvent::to_repr(StreamEvent) ->
    Repr

    StreamSink

    type StreamSink derive(
    Debug
    )

    Mutable sink that converts tokenizer tokens into coalesced stream events.

    StreamSink::to_repr

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn StreamSink::to_repr(StreamSink) ->
    Repr

    StreamStartEvent

    pub(all) struct StreamStartEvent {
    name : String
    attrs : Map[String, String?]
    } derive(Eq,
    Debug
    )

    Start-tag event emitted by the streaming tokenizer facade.

    name is the normalized tag name and attrs contains the decoded attributes for the tag.

    StreamStartEvent::equal

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn StreamStartEvent::equal(StreamStartEvent, StreamStartEvent) -> Bool

    StreamStartEvent::not_equal

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn StreamStartEvent::not_equal(x : StreamStartEvent, y : StreamStartEvent) -> Bool

    StreamStartEvent::to_repr

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn StreamStartEvent::to_repr(StreamStartEvent) ->
    Repr

    stream

    fn stream(html : StringView) -> Array[StreamEvent]

    Parse an HTML string into streaming events.

    stream_bytes

    fn stream_bytes(input : BytesView, encoding? : String) -> Array[StreamEvent]

    Decode HTML bytes and return streaming events.

    When encoding is omitted, the same byte-sniffing path used by parse_bytes chooses the input encoding.

    stream_bytes_each

    fn stream_bytes_each(input : BytesView, emit : (StreamEvent) -> Unit, encoding? : String) -> Unit

    Decode HTML bytes and emit streaming events incrementally.

    When encoding is omitted, the same byte-sniffing path used by parse_bytes chooses the input encoding.

    stream_each

    fn stream_each(html : StringView, emit : (StreamEvent) -> Unit) -> Unit

    Parse an HTML string and emit streaming events incrementally.