Packages

A simple tool likes rake

Current section

Files

Jump to
exake lib exake.ex
Raw

lib/exake.ex

defmodule Exake do
defmacro __using__(opts) do
Module.put_attribute(__CALLER__.module, :namespace, opts[:namespace])
quote do
import unquote(__MODULE__)
end
end
@all_alias [?a..?z, ?A..?Z]
|> Enum.flat_map(& &1)
|> Enum.map(&[&1])
|> Enum.map(&to_string/1)
|> Enum.map(&String.to_atom/1)
defp get_default_value_define(value) when is_integer(value), do: {:integer, value}
defp get_default_value_define(value) when is_float(value), do: {:float, value}
defp get_default_value_define(value) when is_binary(value), do: {:string, value}
defp get_default_value_define(:boolean), do: {:boolean, false}
defp get_default_value_define(), do: {:string, nil}
defp get_arg_define({name, _, _}) when is_atom(name),
do: {name, nil, get_default_value_define()}
defp get_arg_define({name, alias}) when alias in @all_alias, do: {name, alias, {:string, nil}}
defp get_arg_define({name, {alias, default}}) when alias in @all_alias do
{name, alias, get_default_value_define(default)}
end
defp get_arg_define({name, {:..., _, _}}), do: {name, nil, {:argv, []}}
defp get_arg_define({name, value}), do: {name, nil, get_default_value_define(value)}
defp get_task_name_and_args(name) when is_atom(name), do: {name, []}
defp get_task_name_and_args({name, _, args}) when is_atom(name),
do: {name, args |> Enum.flat_map(&List.wrap/1) |> Enum.map(&get_arg_define/1)}
defp get_module_name(name, namespace) do
name =
{namespace, "#{name}"}
|> case do
{nil, name} -> name
{ns, "default"} -> "#{ns}"
{ns, name} -> "#{ns}.#{name}"
end
|> Mix.Utils.command_to_module_name()
Module.concat(Mix.Tasks, name)
end
defp get_args_ast(args) do
get_args_ast =
args
|> Enum.map(fn
{name, _, {_, nil}} ->
quote do
unquote({name, [], nil}) = unquote({:argv, [], nil})[unquote(name)]
end
{name, _, {:argv, value}} ->
quote do
unquote({name, [], nil}) =
unquote({:argv, [], nil})[:__argv__] || unquote({:argv, [], nil})[unquote(name)] ||
unquote(value)
end
{name, _, {_, value}} ->
quote do
unquote({name, [], nil}) = unquote({:argv, [], nil})[unquote(name)] || unquote(value)
end
end)
{:__block__, [], get_args_ast}
end
defp get_parse_options(args) do
parser_switches =
Enum.map(args, fn
{_, _, {:argv, _}} ->
nil
{name, _, {type, _}} ->
{name, type}
end)
|> Enum.filter(& &1)
parser_aliases =
Enum.map(args, fn
{_, nil, _} ->
nil
{name, alias, _} ->
{alias, name}
end)
|> Enum.filter(& &1)
[strict: parser_switches, aliases: parser_aliases]
end
defp get_run_deps_args(deps) do
deps
|> Enum.map(fn
{name, _, [args]} -> {name, args}
name -> {name, []}
end)
end
defmacro task(name, opts \\ [], do: ast) do
mod = __CALLER__.module
deps = opts[:with] || []
{name, args} = get_task_name_and_args(name)
name = get_module_name(name, Module.get_attribute(mod, :namespace))
get_args_ast = get_args_ast(args)
parser_opts = get_parse_options(args)
quote do
defmodule unquote(name) do
use Mix.Task
@shortdoc Module.get_attribute(unquote(mod), :shortdoc)
@moduledoc Module.get_attribute(unquote(mod), :taskdoc)
def run([:internel | argv]) do
# define args variable
var!(argv) = argv
_ = var!(argv)
unquote(get_args_ast)
# run deps
for {dep, args} <- unquote(get_run_deps_args(deps)) do
Mix.Task.run("#{dep}", [:internel | args])
end
unquote(ast)
end
def run(argv) do
# parse options
{parsed, argv, _} = OptionParser.parse(argv, unquote(parser_opts))
run([:internel, {:__argv__, argv} | parsed])
end
end
@shortdoc nil
@taskdoc nil
end
end
end