defmodule Patch.Mock.Code.Generators.Facade do
@moduledoc """
Generator for `facade` modules.
`facade` modules are generated by taking the `target` module and creating a stub function for
each function in the module that calls the `delegate` modules corresponding function.
The `facade` module can optionally expose private functions as public.
"""
alias Patch.Mock.Naming
alias Patch.Mock.Code
alias Patch.Mock.Code.Transform
@generated [generated: true]
@doc """
Generates a new facade module based on the forms of the provided module.
"""
@spec generate(
abstract_forms :: [Code.form()],
module :: module(),
exports :: Code.exports()
) :: [Code.form()]
def generate(abstract_forms, module, exports) do
abstract_forms
|> Transform.clean()
|> Transform.export(exports)
|> Transform.filter(exports)
|> module(module)
end
## Private
defp arguments(0) do
[]
end
defp arguments(arity) do
Enum.map(1..arity, &{:var, @generated, :"_arg#{&1}"})
end
defp body(module, name, arity) do
delegate = Naming.delegate(module)
[
{
:call,
@generated,
{:remote, @generated, {:atom, @generated, delegate}, {:atom, @generated, name}},
arguments(arity)
}
]
end
defp function(module, name, arity) do
clause = {
:clause,
@generated,
patterns(arity),
[],
body(module, name, arity)
}
{:function, @generated, name, arity, [clause]}
end
@spec module(abstract_forms :: [Code.form()], module :: module()) ::
[Code.form()]
defp module(abstract_forms, module) do
Enum.map(abstract_forms, fn
{:function, _, name, arity, _} ->
function(module, name, arity)
other ->
other
end)
end
defp patterns(0) do
[]
end
defp patterns(arity) do
Enum.map(1..arity, fn position ->
{:var, @generated, :"_arg#{position}"}
end)
end
end