Packages

Elixir library for translating between bottom and human-readable text.

Current section

Files

Jump to
bottom lib bottom.ex
Raw

lib/bottom.ex

defmodule Bottom do
@moduledoc """
This is an Elixir implementation of [bottom](https://github.com/kaylynn234/bottom).
Bottom encodes UTF-8 text into a sequence comprised of bottom emoji
(`πŸ«‚βœ¨πŸ₯Ίβ€οΈ`, with , sprinkled in for good measure) followed by `πŸ‘‰πŸ‘ˆ`.
It can encode any valid UTF-8 - being a bottom transcends language,
after all - and decode back into UTF-8.
"""
@character_values [
{200, "πŸ«‚"},
{50, "πŸ’–"},
{10, "✨"},
{5, "πŸ₯Ί"},
{1, ","},
{0, "❀️"}
]
@sep "πŸ‘‰πŸ‘ˆ"
@doc ~S"""
Encodes UTF-8 text into bottom emoji.
## Examples
iex> Bottom.encode("Test")
{:ok, "πŸ’–βœ¨βœ¨βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ"}
iex> Bottom.encode("Hello world!")
{:ok, "πŸ’–βœ¨βœ¨,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ₯Ί,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ₯Ί,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨,πŸ‘‰πŸ‘ˆβœ¨βœ¨βœ¨,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ₯Ί,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ‘‰πŸ‘ˆβœ¨βœ¨βœ¨,,,πŸ‘‰πŸ‘ˆ"}
iex> Bottom.encode("γŒγ‚“γ°γ‚Œ")
{:ok, "πŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆ"}
"""
def encode(text) do
encoded =
text
|> :binary.bin_to_list()
|> Enum.map(&encode_byte/1)
|> Enum.map(&Enum.join/1)
|> Enum.map(fn x -> x <> @sep end)
|> Enum.join()
{:ok, encoded}
end
@doc ~S"""
Decodes a sequence of bottom emoji into UTF-8 text. Returns `{:error, :invalid}` if
text is not a valid sequence of emojis.
## Examples
iex> Bottom.decode("πŸ’–βœ¨βœ¨βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ")
{:ok, "Test"}
iex> Bottom.decode("πŸ’–βœ¨βœ¨,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ₯Ί,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ₯Ί,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨,πŸ‘‰πŸ‘ˆβœ¨βœ¨βœ¨,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ₯Ί,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ‘‰πŸ‘ˆβœ¨βœ¨βœ¨,,,πŸ‘‰πŸ‘ˆ")
{:ok, "Hello world!"}
iex> Bottom.decode("πŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆ")
{:ok, "γŒγ‚“γ°γ‚Œ"}
iex> Bottom.decode("πŸ˜€πŸ˜ƒπŸ˜„πŸ˜πŸ˜†πŸ˜…πŸ˜‚")
{:error, :invalid}
"""
def decode(text) do
decoded =
text
|> String.trim_trailing(@sep)
|> String.split(@sep)
|> Enum.map(&decode_byte/1)
|> :binary.list_to_bin()
{:ok, decoded}
rescue
_ -> {:error, :invalid}
end
defp encode_byte(value) do
case value do
0 ->
{_, emoji} = Enum.find(@character_values, fn {num, _} -> num == 0 end)
[emoji]
value ->
_encode_byte(value)
end
end
defp _encode_byte(value) do
case value do
0 ->
[]
value ->
{max, emoji} = Enum.find(@character_values, fn {num, _} -> value >= num end)
[emoji | _encode_byte(value - max)]
end
end
defp decode_byte(text) do
text
|> String.codepoints()
|> Enum.map(&Enum.find(@character_values, fn {_, emoji} -> emoji == &1 end))
|> Enum.map(fn {num, _} -> num end)
|> Enum.sum()
end
end