lib/utils/lists.ex

defmodule Matcher.Utils.Lists do
  import Matcher.Errors

  alias Matcher.Context

  def is_enumerable(value) do
    Enumerable.impl_for(value) != nil
  end

  def permutations([]), do: [[]]

  def permutations(list),
    do: for(elem <- list, rest <- permutations(list -- [elem]), do: [elem | rest])

  def compare_lists(expected, actual, context) do
    if length(expected) != length(actual) do
      error(context,
        type: :list_length_differ,
        message:
          "expected length #{expected(length(expected))} but got #{mismatched(length(actual))}"
      )
    else
      Enum.zip(expected, actual)
      |> Enum.reduce_while(0, fn {e, a}, index ->
        case Matcher.match(e, a, Context.append_path(context, index)) do
          {:ok, _} -> {:cont, index + 1}
          error -> {:halt, error}
        end
      end)
      |> case do
        {:error, error} ->
          {:error, error}

        _i ->
          {:ok, nil}
      end
    end
  end
end