Cleanly add custom errors for Ecto.Changeset.cast/4

defmodule PhoneNumberType do
  use Ecto.Type

  def type, do: :string

  def cast(value) do
    case Ecto.Type.cast(:string, value) do
      {:ok, value} -> {:ok, value}
      :error -> {:error, "Our custom phone number error message here"}
    end
  end

  def dump(value), do: Ecto.Type.dump(:string, value)
  
  def load(value), do: Ecto.Type.load(:string, value)
end

defmodule UnionStruct.PhoneNumber do
  use Ecto.Schema

  embedded_schema do
    field :other_data, :any
    field :value, PhoneNumberType
  end

  def changeset(changeset, params, opts) do
    Ecto.Changeset.cast(changeset, params, [:value], opts)
    |> validate_phone_number(:value)
  end
end

You can wrap generic types like this to get more specific types. If you have many similar ones you can even abstract this using Ecto.ParameterizedType.

1 Like