-module(rebar3_erlquery_prv).
-export([init/1, do/1, format_error/1]).
-define(PROVIDER, compile).
-define(DEPS, [{default, compile}]).
%% ===================================================================
%% Public API
%% ===================================================================
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
Provider =
providers:create([{name, ?PROVIDER}, % The 'user friendly' name of the task
{module, ?MODULE}, % The module implementation of the task
{namespace, erlquery},
{bare,
false}, % The task can be run by the user, always true
{deps, ?DEPS}, % The list of dependencies
{example, "Config in rebar.config"}, % How to use the plugin
{opts, []}, % list of options understood by the plugin
{short_desc, "A rebar plugin for compiling erlquery files"},
{desc, "A rebar plugin for compiling erlquery files"}]),
{ok, rebar_state:add_provider(State, Provider)}.
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
rebar_api:info("Running erlquery...", []),
Apps =
case rebar_state:current_app(State) of
undefined ->
rebar_state:project_apps(State);
AppInfo ->
[AppInfo]
end,
[begin
Opts = rebar_app_info:opts(AppInfo),
Dir = rebar_app_info:dir(AppInfo),
OutDir = rebar_app_info:out_dir(AppInfo),
EbinDir = rebar_app_info:ebin_dir(AppInfo),
filelib:ensure_dir(
filename:join(EbinDir, "dummy.beam")),
filelib:ensure_dir(
filename:join(OutDir, "dummy.erl")),
TargetExt = ".erl",
SourceExt = ".erlq",
rebar_base_compiler:run(Opts,
[],
Dir,
SourceExt,
OutDir,
TargetExt,
fun(S, T, _C) -> do_compile(S, T, Dir, EbinDir) end)
end
|| AppInfo <- Apps],
{ok, State}.
-spec format_error(any()) -> iolist().
format_error(Reason) ->
io_lib:format("~p", [Reason]).
do_compile(Source, Target, SourceDir, BinDir) ->
InputFile = filename:join(SourceDir, Source),
case erlq_codegen(InputFile) of
{ok, EBin} ->
NewTarget = bin_filename(Target),
NewPath = filename:join(BinDir, NewTarget),
file:write_file(NewPath, EBin);
{error, _Err} = E ->
{error, ferror(E), []}
end.
-spec erlq_codegen(binary()) -> {ok, binary()} | {error, any()}.
erlq_codegen(InputFile) ->
case file:read_file(InputFile) of
{ok, SourceBin} ->
case erlquery:parse(SourceBin) of
{ok, Config} ->
case erlquery:codegen(Config) of
{ok, Forms} ->
case compile:forms(Forms, [debug_info]) of
{ok, _ModuleName, EBin} ->
{ok, EBin};
E ->
{error, E}
end;
{error, _Err} = E ->
E
end;
{error, _Err} = E ->
E
end;
{error, _Err} = E ->
E
end.
-spec bin_filename(binary()) -> string().
bin_filename(Target) ->
string:concat(
filename:rootname(
filename:basename(Target), ".erl"),
".beam").
-spec ferror({error, {atom(), binary()}} |
{error, {atom(), {integer(), binary()}}} |
{error, {invalid_query_arity, {integer(), binary()}}} |
{error, any()} |
{error, {atom(), any()}}) ->
[string()].
ferror({error, {invalid_query_arity, {Line, <<Data/binary>>}}}) ->
[io_lib:format("~nerlquery compiler error - invalid query arity, expected 2 - line ~p:~n~n\t~s~n",
[Line, Data])];
ferror({error, {Reason, {Line, <<Data/binary>>}}}) ->
[io_lib:format("~nerlquery compiler error - ~p - line ~p:~n~n\t~s~n",
[Reason, Line, Data])];
ferror({error, {Reason, <<Data/binary>>}}) ->
[io_lib:format("erlquery compiler error - ~p:~n~n\t~s~n", [Reason, Data])];
ferror({error, {Reason, Data}}) ->
[io_lib:format("erlquery compiler error - ~p:~n~n\t~p~n", [Reason, Data])];
ferror({error, Err}) ->
[io_lib:format("erlquery compiler error:~n~p", [Err])].