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>" iex> Braintree.XML.Entity.encode("Here & There") "Here & There" """ @spec encode(String.t()) :: String.t() def encode(string) do string |> String.graphemes() |> Enum.map(&escape/1) |> Enum.join() 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: <> defp replace(_, "#" <> code), do: <> 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