lib/ex_oapi/parser/context/callback/callback_map.ex

defmodule ExOAPI.Parser.V3.Context.Callback.Map do
  @behaviour Ecto.Type

  import Ecto.Changeset, only: [apply_action: 2]

  alias ExOAPI.Parser.V3.Context

  @type t() :: %{String.t() => %{String.t() => Context.Paths.t()}}

  @impl Ecto.Type
  def type, do: :map

  @impl Ecto.Type
  def load(data), do: cast(data)

  @impl Ecto.Type
  def cast(data) when is_map(data) do
    Enum.reduce_while(data, {:ok, %{}}, fn {k, v}, {_, acc} ->
      Enum.reduce_while(v, {:ok, %{}}, fn {i_k, i_v}, {_, i_acc} ->
        with changeset <- Context.Link.map_cast(i_v),
             {:ok, applied} <- apply_action(changeset, :insert) do
          {:cont, {:ok, Map.put(i_acc, i_k, applied)}}
        else
          {:error, changeset} ->
            {:halt, {:error, {i_k, i_v, changeset}}}
        end
      end)
      |> case do
        {:ok, i_acc} -> {:cont, {:ok, Map.put(acc, k, i_acc)}}
        {:error, _} = error -> {:halt, error}
      end
    end)
    |> case do
      {:ok, _} = ok ->
        ok

      {:error, {k, v, changeset}} ->
        raise "Error casting Schema: #{inspect(k)} -> #{inspect(v)} resulting in #{inspect(changeset)}"
    end
  end

  def cast(_), do: :error

  @impl Ecto.Type
  def dump(data) when is_map(data), do: {:ok, data}
  def dump(nil), do: {:ok, %{}}
  def dump(_), do: :error

  @impl Ecto.Type
  def equal?(a, a), do: true
  def equal?(_, _), do: false

  @impl Ecto.Type
  def embed_as(_), do: :self
end