defmodule Private do
@moduledoc File.read!("README.md")
defmacro __using__(_opts) do
quote do
require Private
import Private, only: [ private: 1, private: 2 ]
end
end
@doc """
Define private functions:
private do
def ...
defp ...
end
All functions in the block will be defined as public if Mix.env is `:test`,
private otherwise. `def` and `defp` are effectively the same in the block.
See the documentation for the `Private` module for more information.
"""
defmacro private(do: block) do
quote do
if Code.ensure_loaded?(Mix) do
unquote(do_private(block, Mix.env))
else
unquote(block)
end
end
end
@doc false && """
Define private functions:
private(env) do
def ...
defp ...
end
All functions in the block will be defined as public if env is `:test`,
private otherwise. This is only provided for my own testing.
"""
defmacro private(env, do: block) do
quote do
unquote(do_private(block, env))
end
end
defp do_private(block, _env = :test) do
make_defs_public(block)
end
defp do_private(block, _env) do
make_defs_private(block)
end
defp make_defs_private(block) do
Macro.traverse(block, nil, &make_private/2, &identity/2)
end
defp make_defs_public(block) do
Macro.traverse(block, nil, &make_public/2, &identity/2)
end
defp make_private({:def, meta, code}, acc) do
{ {:defp, meta, code}, acc }
end
defp make_private(ast, acc), do: identity(ast, acc)
defp make_public({:defp, meta, code}, acc) do
{ {:def, meta, code}, acc }
end
defp make_public(ast, acc), do: identity(ast, acc)
defp identity(ast, acc) do
{ ast, acc }
end
end