defmodule XmlBuilder.Element do @moduledoc """ The internal representation of the _XML_ element, responsible for parsing _and_ normalization. Generally, you won’t use this internal module. """ @typedoc "Name of the _XML_ element" @type name :: bitstring() | binary() | atom() @typedoc "Attributes of the _XML_ element" @type attrs :: map() | nil @typedoc "Single inner subelement of the _XML_ element" @type content :: tuple() | String.t() @typedoc "Whole inner content of the _XML_ element" @type contents :: [content()] | nil @typedoc "The internal representation of the _XML_ element" @type t :: %__MODULE__{ name: name(), attrs: attrs(), content: contents() } @typedoc "The raw internal representation of the _XML_ element" @type as_tuple :: {name(), attrs(), contents()} @typedoc "The AST representation of _XML_" @type ast :: as_tuple() | [as_tuple()] defstruct name: nil, attrs: %{}, content: nil alias XmlBuilder.Element, as: E @doc false @spec tuple(t()) :: as_tuple() def tuple(%E{name: name, attrs: attrs, content: content}), do: {name, attrs, content} @doc false @spec element( {:iodata, iodata()} | bitstring() | atom() | list() | {name()} | {name(), attrs() | contents()} | {name(), attrs(), contents()} | {nil, nil, {:iodata, iodata()}} | {nil, nil, bitstring()} ) :: as_tuple() def element({:iodata, _data} = iodata), do: element({nil, nil, iodata}) def element(name) when is_bitstring(name), do: element({nil, nil, name}) def element(name) when is_atom(name), do: element({name}) def element(list) when is_list(list), do: list |> Enum.reject(&is_nil/1) |> Enum.map(&element/1) def element({name}), do: element({name, nil, nil}) def element({name, attrs}) when is_map(attrs), do: element({name, attrs, nil}) def element({name, content}), do: element({name, nil, content}) def element({name, attrs, content}) when is_list(content) do %E{name: name, attrs: attrs, content: Enum.map(content, &element/1)} |> E.tuple() end def element({name, attrs, content}), do: {name, attrs, content} @doc false @spec element(name(), attrs() | contents()) :: as_tuple() def element(name, attrs) when is_map(attrs), do: element({name, attrs, nil}) def element(name, content), do: element({name, nil, content}) @doc false @spec element(name(), attrs(), contents()) :: as_tuple() def element(name, attrs, content), do: element({name, attrs, content}) end