lib/field_comparator.ex

defmodule FieldComparator do
  @moduledoc """
    Prepare validators to compare the input fields
  """

  def prepare(data, validators, validator_mappings) do
    validators
    |> Enum.map(fn [path, path_to_value, validator] ->
      {path, prepare_validator(validator, path_to_data(path_to_value, data), validator_mappings)}
    end)
  end

  defp prepare_validator(validator, data, mappings) do
    validator
    |> Enum.map(fn
      {validator_name, validator_options} ->
        {validator_name,
         get_validator(validator_name, mappings).to_comparator(data, validator_options)}

      validator_name ->
        {validator_name, get_validator(validator_name, mappings).to_comparator(data, nil)}
    end)
  end

  defp path_to_data([path_part | []], %{} = data), do: Map.get(data, path_part)

  defp path_to_data([path_part | path], %{} = data),
    do: path_to_data(path, Map.get(data, path_part))

  defp path_to_data([elem], data) when is_list(data) and is_integer(elem),
    do: Enum.fetch!(data, elem)

  defp get_validator(name, validators) when is_atom(name), do: validators[name]
end