defmodule Matcher.Utils do def matched?({:ok, _}), do: true def matched?(_), do: false def is_enumerable(value) do Enumerable.impl_for(value) != nil end def compare_maps(expected, actual, allow_unexpected?: allow_unexpected?) when is_map(expected) and is_map(actual) do expected_results = Enum.map(expected, fn {key, matcher} -> find_value(key, matcher, actual) end) unexpected = Map.keys(actual) -- Map.keys(expected) if Enum.all?(expected_results, fn {_, _, _, match} -> matched?(match) end) and (Enum.empty?(unexpected) or allow_unexpected?) do {:ok, nil} else {:error, %{}} end end def compare_maps(e, a, _), do: {:error, %{error: "expected #{inspect(e)}, but got #{inspect(a)}"}} defp find_value(key, matcher, actual_map) do actual_value = Map.get(actual_map, key) match = Matcher.match(matcher, actual_value) {key, matcher, actual_value, match} end def permutations([]), do: [[]] def permutations(list), do: for(elem <- list, rest <- permutations(list -- [elem]), do: [elem | rest]) def compare_lists([], []), do: {:ok, %{}} def compare_lists([_ | _], []), do: {:error, %{}} def compare_lists([], [_ | _]), do: {:error, %{}} def compare_lists([eh | et], [ah | at]) do case Matcher.match(eh, ah) do {:ok, _} -> Matcher.match(et, at) error -> error end end def compare_lists(e, a), do: {:error, %{error: "expected #{inspect(e)}, but got #{inspect(a)}"}} end