Current section
Files
Jump to
Current section
Files
lib/annex/debug.ex
defmodule Annex.Debug do
@moduledoc """
A module for running checks and getting helpful messages.
"""
defmacro __using__(opts \\ []) do
quote do
opts = unquote(opts)
debug_was_passed? = Keyword.get(opts, :debug, false)
debug_was_configured? =
Application.get_env(:annex, __MODULE__, [])
|> Keyword.get(:debug, false)
annex_debug = debug_was_passed? || debug_was_configured? || false
:ok = Module.put_attribute(__MODULE__, :annex_debug, annex_debug)
import Annex.Debug, only: [debug: 1, debug_assert: 2]
def __annex__(:debug), do: @annex_debug
end
end
defmacro debug_assert(reason, do: expr) do
if Module.get_attribute(__CALLER__.module, :annex_debug, false) do
code = Macro.to_string(expr)
vars = Annex.Debug.get_ast_vars(expr)
quote do
result = unquote(expr)
if result != true do
reason = unquote(reason)
vars = unquote(vars)
code = unquote(code)
variables = Keyword.take(binding(), vars)
message = """
Debug assertion failed! -
reason:
#{inspect(reason)}
where:
#{Annex.Debug.format_variables(variables)}
code:
#{Annex.Debug.format_code(code)}
expected:
true
got:
#{inspect(result)}
"""
raise Annex.AnnexError, message: message
end
end
end
end
defmacro debug(do: block) do
if Module.get_attribute(__CALLER__.module, :annex_debug, false) do
block
else
:ok
end
end
@doc false
def get_ast_vars(ast) do
do_get_ast_vars(ast, [])
end
defp do_get_ast_vars(ast, acc) do
case ast do
{name, _, atom} when is_atom(atom) ->
[name | acc]
{_, _, list} when is_list(list) ->
Enum.reduce(list, acc, fn item, more_acc ->
do_get_ast_vars(item, more_acc)
end)
_ ->
acc
end
end
@doc false
def format_code(code) do
code
|> String.split("\n")
|> case do
[^code] ->
code
lines ->
lines
|> Enum.reject(fn l ->
String.trim(l) in ["(", ")"]
end)
|> Enum.join("\n")
|> String.trim()
end
end
def format_variables(keywords) do
keywords
|> Enum.map(fn {k, v} -> " #{k} = #{inspect(v, limit: 5)}" end)
|> Enum.join("\n")
|> String.trim()
end
end