Current section

Files

Jump to
rebar3_gpb_rpc_plugin src gpb_rpc_compile.erl
Raw

src/gpb_rpc_compile.erl

-module(gpb_rpc_compile).
-include_lib("gpb/include/gpb.hrl").
%% API
-export([ file/4 ]).
-define(EMPTY_LINE, "\n\n").
file(ProtoFile, ErlTpl, HrlTpl, GpbRpcOpts) ->
case file:read_file(ProtoFile) of
{ok, Binary} ->
String = binary_to_list(Binary),
ProtoName = filename:rootname(filename:basename(ProtoFile)),
case parse_lines(ProtoName, String) of
{ok, ScanProtoResult}->
file(ProtoName, ErlTpl, HrlTpl, GpbRpcOpts, ScanProtoResult);
Err -> Err
end;
Err -> Err
end.
file(ProtoName, ErlTpl, HrlTpl, GpbRpcOpts, ScanProtoResult) ->
TargetErlDir = proplists:get_value(o_erl, GpbRpcOpts),
TargetHrlDir = proplists:get_value(o_hrl, GpbRpcOpts),
ModuleNameSuffix = proplists:get_value(module_name_suffix, GpbRpcOpts),
PrefixLen = length(proplists:get_value(msg_prefix, GpbRpcOpts)),
ModPrefix = proplists:get_value(mod_prefix, GpbRpcOpts),
ErlTarget = filename:join([TargetErlDir, ProtoName ++ ".erl"]),
HrlTarget = filename:join([TargetHrlDir, ProtoName ++ ".hrl"]),
gen_mod(ProtoName, ErlTpl, ModuleNameSuffix, PrefixLen, ModPrefix, ErlTarget, ScanProtoResult),
gen_hrl(ProtoName, HrlTpl, HrlTarget, ScanProtoResult),
ok.
parse_lines(ProtoName, String) ->
case gpb_scan:string(String) of
{ok, Tokens, _} ->
case gpb_parse:parse(Tokens ++ [{'$end', 99999}]) of
{ok, Result} ->
{ok, Result};
{error, {LNum, _Module, EMsg} = Reason} ->
rebar_api:abort("Parse error on line ~w:~n~p", [LNum, {Tokens, EMsg}]),
{error, {parse_error, ProtoName, Reason}}
end;
{error, Reason} ->
rebar_api:abort("Scan error:~n~p", [Reason]),
erlang:error({scan_error, ProtoName, Reason})
end.
%%--------------------------------------------------------------------
gen_mod(ProtoName, ErlTpl, ModuleNameSuffix, PrefixLen, ModPrefix, Target, ScanProtoResult) ->
Service = list_to_atom(ProtoName ++"_service"),
case lists:keyfind({service, Service}, 1, ScanProtoResult) of
{_, RpcList} ->
RenderData = gen_mod_do(ProtoName, ModuleNameSuffix, PrefixLen, ModPrefix, RpcList),
IoData = bbmustache:compile(ErlTpl, RenderData, [{key_type, atom}]),
file:write_file(Target, IoData);
false ->
rebar_api:debug("skipped gen gpb rpc : ~p", [ProtoName])
end.
gen_mod_do(ProtoName, ModuleNameSuffix, PrefixLen, ModPrefix, RpcList0) ->
GpbProto = ProtoName ++ ModuleNameSuffix,
HandleMod = ModPrefix++string:substr(ProtoName, PrefixLen+1),
ProtoNameUpper = string:to_upper(ProtoName),
BaseData = [
{proto_name, ProtoName},
{proto_name_upper, ProtoNameUpper},
{gpb_proto, GpbProto},
{handle_mod, HandleMod}
],
{CallbackList, RpcList, InputList, OutputList, _} =
lists:foldl(
fun(Rpc, Acc) ->
erl_rpc(Rpc, Acc, BaseData)
end, {[], [], [], [], []}, RpcList0),
[
{proto_name, ProtoName},
{proto_name_upper, ProtoNameUpper},
{gpb_proto, GpbProto},
{handle_mod, HandleMod},
{callback_list, CallbackList},
{rpc_list, RpcList},
{input_list, InputList},
{output_list, OutputList}|BaseData
].
erl_rpc({Func0, {[undefined], _}, {[Output0], _}, _}, {CallbackList, RpcList, InputList, OutputList, OutputListAcc}, BaseData) ->
Func = atom_to_list(Func0),
Output = atom_to_list(Output0),
OutputUpper = string:to_upper(Output),
RpcData = [
{output, Output},{output_upper, OutputUpper},
{handle_func, Func}|BaseData
],
case lists:member(Output0, OutputListAcc) of
true ->
{
CallbackList,
RpcList,
InputList,
OutputList,
OutputListAcc
};
false ->
{
CallbackList,
RpcList,
InputList,
[RpcData|OutputList],
[Output0|OutputListAcc]
}
end;
erl_rpc({Func0, {[Input0], _}, {[undefined], _}, _}, {CallbackList, RpcList, InputList, OutputList, OutputListAcc}, BaseData) ->
Func = atom_to_list(Func0),
Input = atom_to_list(Input0),
InputUpper = string:to_upper(Input),
NewCallbackList = [{handle_func, Func}|CallbackList],
RpcData = [
{input, Input},{input_upper, InputUpper},
{handle_func, Func}|BaseData
],
{
NewCallbackList,
RpcList,
[RpcData|InputList],
OutputList,
OutputListAcc
};
erl_rpc({Func0, {[Input0], _}, {[Output0], _}, _}, {CallbackList, RpcList, InputList, OutputList, OutputListAcc}, BaseData) ->
Func = atom_to_list(Func0),
Input = atom_to_list(Input0),
InputUpper = string:to_upper(Input),
Output = atom_to_list(Output0),
OutputUpper = string:to_upper(Output),
HandleFunc = [{handle_func, Func}],
NewCallbackList = [HandleFunc|CallbackList],
RpcData = [
{input, Input},{input_upper, InputUpper},
{output, Output},{output_upper, OutputUpper},
{handle_func, Func}|BaseData
],
case lists:member(Output0, OutputListAcc) of
true ->
{
NewCallbackList,
[RpcData|RpcList],
InputList,
OutputList,
OutputListAcc
};
false ->
{
NewCallbackList,
[RpcData|RpcList],
InputList,
[RpcData|OutputList],
[Output0|OutputListAcc]
}
end.
%%--------------------------------------------------------------------
gen_hrl(ProtoName, HrlTpl, Target, Result) ->
ProtoNameUpper = string:to_upper(ProtoName),
BaseData = [
{proto_name, ProtoName},
{proto_name_upper, ProtoNameUpper}
],
EnumsList = [ hrl_enums_list(EnumName, EnumList, BaseData) || {{enum, EnumName}, EnumList} <- Result],
RenderData = [{enums_list, EnumsList}|BaseData],
IoData = bbmustache:compile(HrlTpl, RenderData, [{key_type, atom}]),
file:write_file(Target, IoData).
hrl_enums_list(EnumName, EnumList0, BaseData0) ->
EnumNameUpper = string:to_upper(atom_to_list(EnumName)),
BaseData = [
{enum_name, EnumName},
{enum_name_upper, EnumNameUpper}|BaseData0
],
EnumList = [hrl_enum(EnumKey, EnumValue, BaseData)||{EnumKey, EnumValue} <- EnumList0],
[
{enum_list, EnumList}|BaseData
].
hrl_enum(EnumKey, EnumValue, BaseData) ->
EnumKeyUpper = string:to_upper(atom_to_list(EnumKey)),
[
{enum_key, EnumKey},
{enum_key_upper, EnumKeyUpper},
{enum_value, EnumValue}|BaseData
].