lib/migration.ex

defmodule AshPostgres.Migration do
  @moduledoc "Utilities for use in migrations"

  @doc """
  A utility for creating postgres enums for an Ash enum type.

  In your migration, you can say:

  ```elixir
  def up() do
    AshPostgres.Migration.create_enum(MyEnumType)
  end
  ```

  Attribution:

  This code and example was copied from ecto_enum. I didn't use the library itself
  because it has a lot that would not currently be relevant for Ash.
  https://github.com/gjaldon/ecto_enum

  Must be done manually, as the migration generator will not do it.
  Additionally, altering the type must be done in its own, separate migration, which
  must have `@disable_ddl_transaction true`, as you cannot do this operation
  in a transaction.

  For example:

  ```elixir
  defmodule MyApp.Repo.Migrations.AddToGenderEnum do
    use Ecto.Migration
    @disable_ddl_transaction true

    def up do
      Ecto.Migration.execute "ALTER TYPE gender ADD VALUE IF NOT EXISTS 'other'"
    end

    def down do
      ...
    end
  end
  ```

  Keep in mind, that if you want to create a custom enum type, you will want to add
  ```elixir
  def storage_type(_), do: :my_type_name
  ```
  """
  def create_enum(type, constraints \\ []) do
    if type.storage_type(constraints) == :string do
      raise "Must customize the storage_type for #{type} in order to create an enum"
    end

    types = Enum.map_join(type.values(), ", ", &"'#{&1}'")

    Ecto.Migration.execute(
      "CREATE TYPE #{type.storage_type()} AS ENUM (#{types})",
      "DROP TYPE #{type.storage_type()}"
    )
  end

  def drop_enum(type) do
    if type.storage_type() == :string do
      raise "Must customize the storage_type for #{type} in order to create an enum"
    end

    types = Enum.map_join(type.values(), ", ", &"'#{&1}'")

    Ecto.Migration.execute(
      "DROP TYPE #{type.storage_type()}",
      "CREATE TYPE #{type.storage_type()} AS ENUM (#{types})"
    )
  end
end