Current section
Files
Jump to
Current section
Files
lib/shortuuid.ex
defmodule ShortUUID do
@moduledoc """
ShortUUID is a simple UUID shortener library.
## Installation
1. Add ShortUUID to your list of dependencies in `mix.exs`:
```
def deps do
[{:shortuuid, "~> #{ShortUUID.Mixfile.project[:version]}"}]
end
```
2. Optionally configure the alphabet to be used for encoding
in `config.exs`:
```
config :shortuuid,
alphabet: "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
```
The default alphabet (above) will translate UUIDs to base57 using lowercase and uppercase letters
and digits while avoiding similar-looking characters such as l, 1, I, O and 0.
## Typical usage
Use ShortUUID to shorten UUID strings generated by other libraries such as
[Ecto](https://hexdocs.pm/ecto/Ecto.UUID.html),
[Elixir UUID](https://github.com/zyro/elixir-uuid) and [Erlang UUID](https://github.com/okeuday/uuid).
## Acknowledgments
This project was inspired by [skorokithakis/shortuuid](https://github.com/skorokithakis/shortuuid).
"""
@alphabet (Application.get_env(:shortuuid, :alphabet) ||
"23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
|> String.graphemes |> Enum.sort |> Enum.dedup |> List.to_string
@alphabet_graphemes String.graphemes(@alphabet)
@alphabet_length String.length(@alphabet)
@doc """
Take two integers as arguments and return a pair integers consisting of their quotient and remainder.
## Examples
iex> ShortUUID.divmod(324969006592305634633390616021200786553, 57)
[5701210641970274291813870456512294500, 53]
"""
@spec divmod(Integer.t, Integer.t) :: [Integer.t]
defp divmod(dividend, divisor) do
[div(dividend, divisor), rem(dividend, divisor)]
end
@doc """
Convert the number to a string.
"""
@spec int_to_string(Integer.t, list()) :: [String.t]
defp int_to_string(number, acc \\ [])
defp int_to_string(number, acc) when number > 0 do
[result, remainder] = divmod(number, @alphabet_length)
int_to_string(result, [acc | String.at(@alphabet, remainder)])
end
defp int_to_string(0, acc), do: acc |> to_string
@doc """
Encode a UUID using the chosen alphabet.
## Examples
iex> ShortUUID.encode("2a162ee5-02f4-4701-9e87-72762cbce5e2")
"keATfB8JP2ggT7U9JZrpV9"
"""
@spec encode(String.t) :: String.t
def encode(uuid) do
uuid
|> String.replace(~r/[-{}]/, "")
|> String.downcase
|> String.to_integer(16)
|> int_to_string
end
@doc """
Convert a shortened UUID back to a full size UUID.
## Examples
iex> ShortUUID.decode("keATfB8JP2ggT7U9JZrpV9")
"2a162ee5-02f4-4701-9e87-72762cbce5e2"
"""
@spec decode(String.t) :: String.t
def decode(string) do
string_to_int(string)
|> to_hex_string
|> String.downcase
|> add_padding
|> format_string_to_uuid
end
defp string_to_int(string) do
String.reverse(string)
|> String.graphemes
|> Enum.reduce(0, fn(char, acc) ->
acc * @alphabet_length + Enum.find_index(@alphabet_graphemes, &(&1 == char))
end)
end
defp to_hex_string(number) do
number |> Integer.to_string(16)
end
defp add_padding(string) do
string |> String.pad_leading(32, "0")
end
defp format_string_to_uuid(string) do
"#{time_low(string)}-#{time_mid(string)}-#{time_hi_and_ver(string)}-#{clock_seq(string)}-#{nodeid(string)}"
end
defp time_low(string) do
binary_part(string, 0, 8)
end
defp time_mid(string) do
binary_part(string, 8, 4)
end
defp time_hi_and_ver(string) do
binary_part(string, 12, 4)
end
defp clock_seq(string) do
binary_part(string, 16, 4)
end
defp nodeid(string) do
binary_part(string, 20, 12)
end
end