lib/ecto_shortuuid.ex

defmodule Ecto.ShortUUID do
  alias Ecto.UUID

  @moduledoc """
  An Ecto type for Short UUID strings.
  """

  @behaviour Ecto.Type

  @typedoc """
  A hex-encoded UUID string.
  """
  @type uuid :: <<_::288>>

  @typedoc """
  A base57 ShortUUID string.
  """
  @type shortuuid57 :: <<_::176>>

  @typedoc """
  A raw binary representation of a UUID.
  """
  @type raw :: <<_::128>>

  @doc """
  The Ecto type.
  """
  def type, do: :uuid

  @doc """
  Casts the given input to ShortUUID

  https://hexdocs.pm/ecto/Ecto.Type.html#c:cast/1
  """
  @spec cast(shortuuid57 | uuid | any) :: {:ok, shortuuid57} | :error
  def cast(<<_::176>> = shortuuid) when is_binary(shortuuid) do
    case ShortUUID.decode(shortuuid) do
      {:ok, _} -> {:ok, shortuuid}
      {:error, _} -> :error
    end
  end

  def cast(<<_::288>> = uuid) do
    case UUID.cast(uuid) do
      :error -> :error
      {:ok, casted} -> ShortUUID.encode(casted)
    end
  end

  def cast(_), do: :error

  @doc """
  Loads a binary UUID into a ShortUUID string.

  https://hexdocs.pm/ecto/Ecto.Type.html#c:load/1
  """
  @spec load(binary) :: {:ok, binary} | :error
  def load(uuid) do
    case UUID.load(uuid) do
      {:ok, uuid} -> ShortUUID.encode(uuid)
      _ -> :error
    end
  end

  @doc """
  Dumps the given string representing a ShortUUID or UUID into a binary.

  https://hexdocs.pm/ecto/Ecto.Type.html#c:dump/1
  """
  @spec dump(shortuuid57 | uuid | any) :: {:ok, raw} | :error
  def dump(<<_::288>> = uuid), do: Ecto.UUID.dump(uuid)

  def dump(<<_::176>> = shortuuid) do
    with {:ok, uuid} <- ShortUUID.decode(shortuuid),
         {:ok, binary_uuid} <- Ecto.UUID.dump(uuid) do
      {:ok, binary_uuid}
    else
      _ -> :error
    end
  end

  def dump(_), do: :error

  @doc """
  Ecto v3 callback
  Dictates how the type should be treated inside embeds.

  https://hexdocs.pm/ecto/Ecto.Type.html#c:embed_as/1
  """
  def embed_as(_), do: :self

  @doc """
  Ecto v3 callback
  Checks if two terms are semantically equal.

  https://hexdocs.pm/ecto/Ecto.Type.html#c:equal?/2
  """
  def equal?(term1, term2), do: term1 == term2

  @doc """
  Generates a ShortUUID string based on a version 4 (random) UUID.
  """
  # @spec generate() :: t
  def generate do
    {:ok, shortuuid} =
      UUID.generate()
      |> ShortUUID.encode()

    shortuuid
  end

  # Callback invoked by autogenerate fields.
  @doc false
  def autogenerate, do: generate()
end