lib/bubble_lib/xml/xmerl.ex

defmodule BubbleLib.XML.Xmerl do
  @moduledoc """
  Convert 'plain' tuple notation into full XMerl XML notation
  """
  use BubbleLib.XML.XmerlRecords

  def to_xmerl(content) when is_binary(content) do
    {doc, []} =
      content
      |> :erlang.binary_to_list()
      |> :xmerl_scan.string(quiet: true)

    doc
  end

  def to_xmerl([_, _, _] = content) do
    content
    |> xmerl_element()
  end

  defp xmerl_element(value) when is_binary(value) do
    xmlText(value: value)
  end

  defp xmerl_element([name, attributes, content]) do
    xmlElement(
      name: atomize(name),
      attributes: xmerl_attributes(attributes),
      content: xmerl_content(content)
    )
  end

  defp xmerl_attributes(nil), do: []

  defp xmerl_attributes(attributes) do
    attributes
    |> Enum.map(fn {key, value} ->
      xmlAttribute(name: atomize(key), value: to_string(value))
    end)
  end

  defp xmerl_content(nil), do: []

  defp xmerl_content(value) when is_binary(value) do
    [xmlText(value: value)]
  end

  defp xmerl_content(content) when is_list(content) do
    content
    |> Enum.map(&xmerl_element/1)
  end

  defmodule Export do
    def unquote({:"#xml-inheritance#", [], nil}), do: [:xmerl_xml]
    def unquote({:"#text#", [], [Macro.var(:text, nil)]}), do: HtmlEntities.encode(text)
  end

  def xmerl_to_string(e) do
    "<?xml version=\"1.0\"?>" <> xml = IO.chardata_to_string(:xmerl.export_simple([e], Export))

    xml
  end

  defp atomize(name) when is_atom(name), do: name
  defp atomize(name) when is_binary(name), do: String.to_atom(name)
end