Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 31 additions & 11 deletions lib/html_entities.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ defmodule HtmlEntities do
@external_resource "lib/html_entities_list.txt"

@doc "Decode HTML entities in a string."
@spec decode(String.t) :: String.t
@spec decode(String.t()) :: String.t()
def decode(string) do
decode(string, "")
end
Expand Down Expand Up @@ -67,17 +67,37 @@ defmodule HtmlEntities do
defp decode_entity(_), do: :error

@doc "Encode HTML entities in a string."
@spec encode(String.t) :: String.t
@spec encode(String.t()) :: String.t()
def encode(string) do
for <<x <- string>>, into: "" do
case x do
?' -> "&apos;"
?" -> "&quot;"
?& -> "&amp;"
?< -> "&lt;"
?> -> "&gt;"
_ -> <<x>>
end
encode(string, "")
end

defp encode(<<?&, rest::binary>> = entity, acc) do
case entity do
<<head::bytes-size(4), remaining::binary>> when head in ["&lt;", "&gt;"] ->
encode(remaining, acc <> head)

<<?&, ?a, ?m, ?p, ?;, remaining::binary>> ->
encode(remaining, acc <> "&amp;")

<<head::bytes-size(6), remaining::binary>> when head in ["&quot;", "&apos;"] ->
encode(remaining, acc <> head)

_ ->
encode(rest, acc <> encode_entity(?&))
end
end

defp encode(<<head, rest::binary>>, acc) do
encode(rest, acc <> encode_entity(head))
end

defp encode(<<>>, acc), do: acc

defp encode_entity(?'), do: "&apos;"
defp encode_entity(?"), do: "&quot;"
defp encode_entity(?&), do: "&amp;"
defp encode_entity(?<), do: "&lt;"
defp encode_entity(?>), do: "&gt;"
defp encode_entity(other), do: <<other>>
end
16 changes: 16 additions & 0 deletions test/html_entities_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,20 @@ defmodule HtmlEntitiesTest do
test "Encoding does replace unsafe characters" do
assert encode("'\"&<>") == "&apos;&quot;&amp;&lt;&gt;"
end

test "Encoding when existing escaped characters exist" do
assert encode("this has both unsafe characters &<> and an existing &amp; entity") ==
"this has both unsafe characters &amp;&lt;&gt; and an existing &amp; entity"
end

test "Multiple Encodings of the same string will consistent with one single encoding pass" do
original_string = "this has both unsafe characters &<> and an existing &amp; entity"

first_pass = encode(original_string)
second_pass = encode(first_pass)
third_pass = encode(second_pass)

assert first_pass == second_pass
assert third_pass == second_pass
end
end