Packages

An XML SAX parser and encoder in Elixir.

Current section

Files

Jump to
sax lib saxy partial.ex
Raw

lib/saxy/partial.ex

defmodule Sax.Partial do
alias Sax.Parser
@moduledoc ~S"""
Supports parsing an XML document partially. This module is useful when
the XML document cannot be turned into a `Stream` e.g over sockets.
## Example
iex> {:ok, partial} = Sax.Partial.new(StackHandler, [])
iex> {:cont, partial} = Sax.Partial.parse(partial, "<foo>")
iex> {:cont, partial} = Sax.Partial.parse(partial, "</foo>")
iex> Sax.Partial.terminate(partial)
{:ok,
[
end_document: {},
end_element: "foo",
start_element: {"foo", []},
start_document: []
]}
"""
@enforce_keys [:context_fun]
defstruct @enforce_keys
@opaque t() :: %__MODULE__{
context_fun: function()
}
@doc """
Builds up a `Sax.Partial`, which can be used for later parsing.
"""
@spec new(
handler :: module(),
initial_state :: term(),
options :: Keyword.t()
) :: {:ok, partial :: t()} | {:error, exception :: Sax.ParseError.t()}
def new(handler, initial_state, options \\ [])
when is_atom(handler) do
expand_entity = Keyword.get(options, :expand_entity, :keep)
state = %Sax.State{
prolog: nil,
handler: handler,
user_state: initial_state,
expand_entity: expand_entity
}
with {:halted, context_fun} <- Parser.Prolog.parse(<<>>, true, <<>>, 0, state) do
{:ok, %__MODULE__{context_fun: context_fun}}
end
end
@doc """
Continue parsing next chunk of the document with a partial.
This function can return in 3 ways:
* `{:cont, partial}` - The parsing process has not been terminated.
* `{:halt, user_state}` - The parsing process has been terminated, usually because of fast return.
* `{:error, exception}` - The parsing process has erred.
"""
@spec parse(
partial :: t(),
data :: binary
) ::
{:cont, partial :: t()}
| {:halt, state :: term()}
| {:error, exception :: Sax.ParseError.t()}
def parse(%__MODULE__{context_fun: context_fun} = partial, data)
when is_binary(data) do
case context_fun.(data, true) do
{:halted, context_fun} ->
{:cont, %{partial | context_fun: context_fun}}
{:ok, state} ->
{:halt, state.user_state}
{:error, reason} ->
{:error, reason}
end
end
@doc """
Terminates the XML document parsing.
"""
@spec terminate(partial :: t()) :: {:ok, state :: term()} | {:error, exception :: Sax.ParseError.t()}
def terminate(%__MODULE__{context_fun: context_fun}) do
with {:ok, state} <- context_fun.(<<>>, false) do
{:ok, state.user_state}
end
end
end