Current section
Files
Jump to
Current section
Files
lib/exmatch.ex
defmodule ExMatch do
@external_resource "README.md"
@moduledoc """
Assertions for data equivalence.
#{"README.md" |> File.read!() |> String.split("<!-- EXAMPLES -->") |> Enum.at(1)}
"""
@assertion_error (if Mix.env() in [:test] do
ExMatchTest.AssertionError
else
ExUnit.AssertionError
end)
alias ExMatch.ParseContext
@doc """
Raises if the values don't match and displays what exactly was different.
iex> ExMatch.match([1, a, 3], [1, 2, 3])
iex> 2 = a
"""
defmacro match(left, right) do
do_match(left, right, quote(do: %ExMatch.Options{opts: %{}}))
end
defmacro match(left, right, opts) do
do_match(left, right, options_(opts))
end
defmacro options(item) do
options_(item)
end
defp options_(item) do
ExMatch.Options.parse(item, &parse_ast/2)
end
defp do_match(left, right, opts) do
opts_var = Macro.var(:opts, __MODULE__)
parse_context = %ParseContext{parse_ast: &parse_ast/2, opts: opts_var}
{bindings, left} = ParseContext.parse(left, parse_context)
quote location: :keep do
unquote(opts_var) =
case unquote(opts) do
%ExMatch.Options{opts: opts} ->
opts
other ->
raise "The 3rd opts argument must be built using ExMatch.options/1"
end
unquote(bindings) =
case ExMatch.Match.diff(unquote(left), unquote(right), unquote(opts_var)) do
{diff_left, diff_right} = diff ->
raise unquote(@assertion_error),
left: diff_left,
right: diff_right,
context: {:match, []}
bindings when is_list(bindings) ->
bindings
end
:ok
end
end
defp parse_ast(left, _parse_context)
when is_number(left) or is_bitstring(left) or is_atom(left) do
self =
quote location: :keep do
unquote(left)
end
{[], self}
end
defp parse_ast({var, _, context} = left, _parse_context)
when is_atom(var) and is_atom(context) do
ExMatch.Var.parse(left)
end
defp parse_ast({:when, _, [_binding, _condition]} = left, _parse_context) do
ExMatch.Var.parse(left)
end
defp parse_ast(left, parse_context) when is_list(left) do
ExMatch.List.parse(left, parse_context)
end
defp parse_ast({_, _} = left, parse_context) do
ExMatch.Tuple.parse(left, parse_context)
end
defp parse_ast({:{}, _, _} = left, parse_context) do
ExMatch.Tuple.parse(left, parse_context)
end
defp parse_ast({:%{}, _, _} = left, parse_context) do
ExMatch.Map.parse(left, parse_context)
end
defp parse_ast({:%, _, _} = left, parse_context) do
ExMatch.Struct.parse(left, parse_context)
end
defp parse_ast(left, _parse_context) do
ExMatch.Expr.parse(left)
end
end