Current section
Files
Jump to
Current section
Files
lib/prometheus/injector.ex
defmodule Prometheus.Injector do
@moduledoc """
Prometheus injector.
"""
def inject(callback, env, ast) do
ast
|> Macro.prewalk(fn thing ->
case thing do
{:def, _, _} = defun ->
defun
# otherwise e in RuntimeError will be rewritten
{:in, _, _} = arrow ->
arrow
_ ->
Macro.expand(thing, env)
end
end)
|> inject_(callback)
end
# lambda
def inject_({:fn, fn_meta, [{:->, arrow_meta, [args, do_block]}]}, callback) do
case args do
[] ->
callback.(
{{:., [], [{:fn, fn_meta, [{:->, arrow_meta, [[], do_block]}]}]}, [], []}
)
_ ->
names =
args
|> Enum.map(fn {name, _, _} -> name end)
raise Prometheus.InvalidBlockArityError, args: names
end
end
# do_blocks can be simple calls or defs
def inject_([{:do, {:__block__, [], do_blocks}}], callback) do
do_blocks = List.flatten(do_blocks)
if have_defs(do_blocks) do
Enum.map(do_blocks, &inject_to_def(&1, callback))
else
callback.({:__block__, [], do_blocks})
end
end
# just do
def inject_([{:do, do_block}], callback) do
inject_([{:do, {:__block__, [], [do_block]}}], callback)
end
# implicit try
def inject_([{:do, _do_block} | rest] = all, callback) do
if try_unwrapped?(rest) do
callback.(
quote do
try unquote(all)
end
)
else
raise "Unexpected do block #{inspect(rest)}"
end
end
# single do, or other non-block stuff like function calls
def inject_(thing, callback) do
inject_([{:do, {:__block__, [], [thing]}}], callback)
end
defp try_unwrapped?(block) do
Keyword.has_key?(block, :catch) || Keyword.has_key?(block, :rescue) ||
Keyword.has_key?(block, :after) || Keyword.has_key?(block, :else)
end
defp have_defs(blocks) do
defs_count =
Enum.count(blocks, fn
{:def, _, _} -> true
_ -> false
end)
blocks_count = Enum.count(blocks)
case defs_count do
0 -> false
^blocks_count -> true
_ -> raise "Mixing defs and other blocks isn't allowed"
end
end
defp inject_to_def({:def, def_meta, [head, [do: body]]}, callback) do
{:def, def_meta, [head, [do: callback.(body)]]}
end
defp inject_to_def({:def, def_meta, [head, [{:do, _do_block} | _rest] = all]}, callback) do
{:def, def_meta,
[
head,
[
do:
callback.(
quote do
try unquote(all)
end
)
]
]}
end
end