defmodule Braintree.XML.Entity do
@moduledoc """
XML entity conversion for known entities.
"""
@external_resource entities = Path.join([__DIR__, "../../priv/entities.txt"])
@doc """
Replace all escaped HTML entities, except those that would produce invalid XML
## Examples
iex> Braintree.XML.Entity.decode("<tag>")
"<tag>"
iex> Braintree.XML.Entity.decode("Søren")
"Søren"
iex> Braintree.XML.Entity.decode("Normal")
"Normal"
iex> Braintree.XML.Entity.decode("First & Last")
"First & Last"
iex> Braintree.XML.Entity.decode(""air quotes"")
~s("air quotes")
"""
@spec decode(String.t()) :: String.t()
def decode(string) do
Regex.replace(~r/\&([^\s]+);/U, string, &replace/2)
end
@doc """
Encode all illegal XML characters by replacing them with corresponding
entities.
## Examples
iex> Braintree.XML.Entity.encode("<tag>")
"<tag>"
iex> Braintree.XML.Entity.encode("Here & There")
"Here & There"
"""
@spec encode(String.t()) :: String.t()
def encode(string) do
string
|> String.graphemes()
|> Enum.map_join(&escape/1)
end
for line <- File.stream!(entities) do
[name, character, codepoint] = String.split(line, ",")
defp replace(_, unquote(name)), do: unquote(character)
defp replace(_, unquote(codepoint)), do: unquote(character)
end
defp replace(_, "#x" <> code), do: <<String.to_integer(code, 16)::utf8>>
defp replace(_, "#" <> code), do: <<String.to_integer(code)::utf8>>
defp replace(original, _), do: original
defp escape("'"), do: "'"
defp escape("\""), do: """
defp escape("&"), do: "&"
defp escape("<"), do: "<"
defp escape(">"), do: ">"
defp escape(original), do: original
end