Current section
Files
Jump to
Current section
Files
lib/typeid.ex
defmodule Typeid do
defstruct type: nil, uuid: nil
@moduledoc ~S"""
Documentation for `Typeid`.
"""
@doc ~S"""
Parse a type ID into prefix and suffix.
## Examples
iex> Typeid.decode("user_01h2e8kqvbfwea724h75qc655w")
{:ok, %Typeid{
type: "user",
uuid: "00622722-77da-dfc7-28e2-244e5bb0c52f"}}
iex> Typeid.decode("user_01h2e8kqvbfwea724h75qc655o")
{:error, "Invalid base 32 encoded ID"}
"""
def decode(typeid) do
{type, encoded_id} = typeid |> String.split("_") |> List.to_tuple()
case CrockfordBase32.decode_to_binary(encoded_id) do
{:ok, binary_uuid} ->
{:ok,
%Typeid{
type: type,
uuid: binary_uuid |> Uniq.UUID.to_string()
}}
{:error, _} ->
{:error, "Invalid base 32 encoded ID"}
end
end
@doc ~S"""
Encode a type ID from a type and UUID.
## Examples
iex> Typeid.encode("user", "01889c89-df6b-7f1c-a388-91396ec314bc")
{:ok, "user_01h2e8kqvbfwea724h75qc655w"}
iex> Typeid.encode("user", "0649s2ezddzhs8w8j4wpxgrmqg")
{:ok, "user_0649s2ezddzhs8w8j4wpxgrmqg"}
iex> Typeid.encode("User", "0649s2ezddzhs8w8j4wpxgrmqg")
{:error, "The prefix should be lowercase with no uppercase letters"}
iex> Typeid.encode("User", "0649s2ezddzhs8w8j4wpxgrmqg")
{:error, "The prefix should be lowercase with no uppercase letters"}
iex> Typeid.encode("user", "invalid")
{:error, "Invalid length. ID should have 26 characters, got 7"}
"""
def encode(type) do
encode(type, Uniq.UUID.uuid7())
end
def encode(type, base32id) when byte_size(base32id) == 26 do
cond do
String.downcase(type) != type ->
{:error, "The prefix should be lowercase with no uppercase letters"}
Regex.match?(~r/[0-9]+/, type) ->
{:error, "The prefix can't have numbers, it needs to be alphabetic"}
true ->
{:ok, join(type, base32id)}
end
end
def encode(type, uuid) when byte_size(uuid) == 36 do
cond do
String.downcase(type) != type ->
{:error, "The prefix should be lowercase with no uppercase letters"}
Regex.match?(~r/[0-9]+/, type) ->
{:error, "The prefix can't have numbers, it needs to be alphabetic"}
true ->
eid =
uuid
|> Uniq.UUID.to_string(:hex)
|> String.to_integer(16)
|> CrockfordBase32.encode()
|> String.downcase()
|> String.pad_leading(26, "0")
{:ok, join(type, eid)}
end
end
def encode(_, id) do
{:error, "Invalid length. ID should have 26 characters, got #{String.length(id)}"}
end
defp join(prefix, suffix) do
if prefix == "" do
suffix
else
prefix <> "_" <> suffix
end
end
end