Gumbo.Output (gumbo v0.3.0)

Copy Markdown View Source

The opaque result of Gumbo.parse/2 and Gumbo.parse_fragment/3.

The struct holds one NIF resource — the parse tree, a private copy of the input, the options, and the preorder node table — plus the cached node count. Node structs returned by document/1, root/1, fragment_nodes/1 and the node accessors reference this handle; the handle must outlive them, and the tree is freed when the handle is garbage collected.

The struct is opaque but forgeable, and resource is not checked at construction: a hand-built struct can carry a non-resource term, and even a reference can be a stale one whose tree is gone. That is a programmer error, so every accessor that reads through the resource — document/1, root/1, fragment_nodes/1, errors/1, input/1, options/1, and Gumbo.materialize/2 on an output — raises ArgumentError on it, exactly as the node accessors do on a forged index; the NIF-internal {:error, :invalid_handle} never reaches a caller. node_count/1 reads only the cached struct field and does not raise.

Summary

Functions

The document node (GUMBO_NODE_DOCUMENT), index 0 of the node table.

The parse errors gumbo recorded, in source order; [] when none.

The parsed fragment's nodes: the children of root/1.

The exact input that was parsed.

The number of nodes in the tree, document included.

The options the parse ran with, including the :dirty policy.

The root <html> element — real, or the implied one in fragment mode — or nil when the parse stopped before any element was built.

Types

t()

@opaque t()

Functions

document(output)

@spec document(t()) :: Gumbo.Node.t()

The document node (GUMBO_NODE_DOCUMENT), index 0 of the node table.

Examples

iex> {:ok, output} = Gumbo.parse("<!doctype html><p>x</p>")
iex> Gumbo.Node.type(Gumbo.Output.document(output))
:document

errors(output)

@spec errors(t()) :: [Gumbo.Error.t()]

The parse errors gumbo recorded, in source order; [] when none.

These are descriptions of malformations the parser recovered from: parse/2 still returns {:ok, _} with the best-effort tree. gumbo is designed to be resilient, so an empty list does not mean "valid HTML" — plenty of sloppy but legal markup produces no errors, and not every malformation produces one. The list is capped by the :max_errors option, which defaults to 100, and by :stop_on_first_error. Pass max_errors: -1 for every error gumbo records; a deeply nested malformed document is where that gets expensive, because each error carries a copy of the tag stack.

Examples

iex> {:ok, output} = Gumbo.parse("<!doctype html><p>x</p>")
iex> Gumbo.Output.errors(output)
[]

iex> {:ok, output} = Gumbo.parse(~s(<div class="a" class="b">))
iex> output |> Gumbo.Output.errors() |> Enum.at(0) |> Gumbo.Error.type()
:duplicate_attr

fragment_nodes(output)

@spec fragment_nodes(t()) :: [Gumbo.Node.t()]

The parsed fragment's nodes: the children of root/1.

A fragment parse (Gumbo.parse_fragment/3) is a full gumbo_parse_with_options run, so its root/1 is the parser-inserted implied <html> element rather than the context element — the context element lives in gumbo's internal parser state and is freed with it, so it never appears in the tree. The children of that implied root are the nodes the fragment parse produced, which is what callers almost always want. Outside fragment mode this is the children of the real <html> element.

[] when root/1 is nil: a stop_on_first_error parse that halted before any element was built has no root, so it has no fragment nodes.

Examples

iex> {:ok, output} = Gumbo.parse_fragment("<b>x</b>", :div)
iex> Enum.map(Gumbo.Output.fragment_nodes(output), &Gumbo.Node.tag_name/1)
["b"]

iex> {:ok, output} = Gumbo.parse(String.duplicate("<div title=>", 50), stop_on_first_error: true)
iex> Gumbo.Output.fragment_nodes(output)
[]

input(output)

@spec input(t()) :: binary()

The exact input that was parsed.

Examples

iex> {:ok, output} = Gumbo.parse("<p>x</p>")
iex> Gumbo.Output.input(output)
"<p>x</p>"

node_count(output)

@spec node_count(t()) :: non_neg_integer()

The number of nodes in the tree, document included.

Examples

iex> {:ok, output} = Gumbo.parse("<!doctype html><p>x</p>")
iex> Gumbo.Output.node_count(output)
6

options(output)

@spec options(t()) :: Gumbo.Options.t()

The options the parse ran with, including the :dirty policy.

Examples

iex> {:ok, output} = Gumbo.parse("<p>x</p>", max_errors: 3)
iex> Gumbo.Output.options(output).max_errors
3

root(output)

@spec root(t()) :: Gumbo.Node.t() | nil

The root <html> element — real, or the implied one in fragment mode — or nil when the parse stopped before any element was built.

stop_on_first_error: true can halt the parse that early, and gumbo then has no root element (output->root stays NULL); the errors vector is exactly what a caller stopping on the first error wants, so the handle survives with no root rather than failing the parse.

The root is also not necessarily node table index 1: a comment or processing instruction before <html> is a document child and occupies index 1 instead, so the index is resolved by pointer when the tree is built.

Examples

iex> {:ok, output} = Gumbo.parse("<!doctype html><p>x</p>")
iex> Gumbo.Node.tag(Gumbo.Output.root(output))
:html

iex> {:ok, output} = Gumbo.parse(String.duplicate("<div title=>", 50), stop_on_first_error: true)
iex> Gumbo.Output.root(output)
nil