Current section
Files
Jump to
Current section
Files
src/gpb_rpc_compile.erl
-module(gpb_rpc_compile).
-include_lib("gpb/include/gpb.hrl").
%% API
-export([ file/2 ]).
-define(EMPTY_LINE, "\n\n").
file(ProtoFile, GpbRpcOpts) ->
case file:read_file(ProtoFile) of
{ok, Binary} ->
String = binary_to_list(Binary),
Msg = filename:rootname(filename:basename(ProtoFile)),
case parse_lines(Msg, String) of
{ok, Result}->
file(Msg, GpbRpcOpts, Result);
Err -> Err
end;
Err -> Err
end.
file(Msg, GpbRpcOpts, Result) ->
TargetErlDir = proplists:get_value(o_erl, GpbRpcOpts),
TargetHrlDir = proplists:get_value(o_hrl, GpbRpcOpts),
ModuleNameSuffix = proplists:get_value(module_name_suffix, GpbRpcOpts),
HeaderMsg = proplists:get_value(h_msg, GpbRpcOpts),
PrefixLen = length(proplists:get_value(msg_prefix, GpbRpcOpts)),
ModPrefix = proplists:get_value(mod_prefix, GpbRpcOpts),
ErlTarget = filename:join([TargetErlDir, Msg ++ ".erl"]),
HrlTarget = filename:join([TargetHrlDir, Msg ++ ".hrl"]),
gen_mod(Msg, ModuleNameSuffix, PrefixLen, ModPrefix, HeaderMsg, ErlTarget, Result),
gen_hrl(Msg, HrlTarget, Result),
ok.
parse_lines(FName, 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} ->
io:format("Parse error on line ~w:~n ~p~n",
[LNum, {Tokens,EMsg}]),
{error, {parse_error, FName, Reason}}
end;
{error, Reason} ->
io:format("Scan error:~n ~p~n", [Reason]),
erlang:error({scan_error, FName, Reason})
end.
gen_mod(Msg, ModuleNameSuffix, PrefixLen, ModPrefix, HeaderMsg, Target, Result) ->
Service = list_to_atom(Msg++"_service"),
case lists:keyfind({service, Service}, 1, Result) of
{_, RpcList} ->
{CallbackList, Body} = gen_rpc_list(Msg, ModuleNameSuffix, PrefixLen, ModPrefix, HeaderMsg, RpcList),
MsgPb = Msg++ModuleNameSuffix,
IoData = [
gen_header(Msg, MsgPb), ?EMPTY_LINE,
CallbackList, ?EMPTY_LINE,
Body, ?EMPTY_LINE,
gen_encode_msg(MsgPb, HeaderMsg), ?EMPTY_LINE,
gen_send_msg(), ?EMPTY_LINE,
gen_footer()
],
file:write_file(Target, IoData);
false -> none
end.
gen_hrl(Msg0, Target, Result) ->
Msg = string:to_upper(Msg0),
Body = [ gen_macros(Prefix, EnumList) || {{enum, Prefix}, EnumList} <- Result],
IoData = [gen_hrl_header(Msg), Body, gen_hrl_footer()],
file:write_file(Target, IoData).
gen_hrl_header(Msg) ->
"-ifndef("++Msg++"_H).
-define("++Msg++"_H, true).\n".
gen_macros(Prefix, EnumList) ->
[gen_macro(Prefix, Key, Value)||{Key, Value} <- EnumList].
gen_macro(Prefix, Key, Value) ->
UpperPrefix = string:to_upper(atom_to_list(Prefix)),
UpperKey = string:to_upper(atom_to_list(Key)),
Macro = UpperPrefix++"_"++UpperKey,
MacroKey = UpperPrefix++"_"++UpperKey++"_KEY",
[
io_lib:format("-define(~ts, ~p).%~p~n", [Macro, Value, Key]),
io_lib:format("-define(~ts, ~p).%~p~n", [MacroKey, Key, Value])
].
gen_hrl_footer() ->
"\n-endif.".
gen_header(Msg, MsgPb) ->
UpperMsg = string:to_upper(Msg),
"-module("++ Msg ++").
-include(\"msg.hrl\").
-ifdef(CMD_"++UpperMsg++").
-include(\""++Msg++".hrl\").
-include(\""++MsgPb++".hrl\").
-define(THIS_CMD, ?CMD_"++UpperMsg++").
-export([
handle_msg/3,
decode_input_msg/1,
decode_output_msg/1,
decode_input/2,
decode_output/2,
encode_msg/2,
send_msg/3
]).".
gen_rpc_list(Msg, ModuleNameSuffix, PrefixLen, ModPrefix, HeaderMsg, RpcList) ->
Mod = ModPrefix++string:substr(Msg, PrefixLen+1),
MsgPb = Msg++ModuleNameSuffix,
{[CallbackList, HandleMsg, DecodeInput, DecodeOutput], _} =
lists:foldl(
fun(Rpc, {GenListAcc, OutputList}) ->
gen_rpc(MsgPb, HeaderMsg, Mod, Rpc, GenListAcc, OutputList)
end, {lists:duplicate(4, []), []}, RpcList),
BodyList =
[
HandleMsg,
gen_handle_msg_last(),?EMPTY_LINE,
gen_decode_input_msg(),?EMPTY_LINE,
gen_decode_output_msg(),?EMPTY_LINE,
DecodeInput,
gen_decode_input_last(),?EMPTY_LINE,
DecodeOutput,
gen_decode_output_last(),?EMPTY_LINE
],
{CallbackList,BodyList}.
gen_rpc(MsgPb, HeaderMsg, Mod, {Func0, {[Input0], _}, {[Output0], _}, _}, GenListAcc, OutputList) ->
Func = atom_to_list(Func0),
Input = atom_to_list(Input0),
UpperInput = string:to_upper(Input),
Output = atom_to_list(Output0),
{UpperOutput, NewOutputList} =
case lists:member(Output0, OutputList) of
true ->
{"UNDEFINED", OutputList};
false ->
{string:to_upper(Output), [Output0|OutputList]}
end,
GenList = [
gen_callback(Func, HeaderMsg),
gen_handle_msg(Mod, Func, MsgPb, Input, UpperInput, UpperOutput),
gen_decode_input(Mod, Func, MsgPb, Input, UpperInput),
gen_decode_output(MsgPb, Output, UpperOutput)
],
NewGenListAcc = lists:zipwith(fun(Gen, OldList) -> [Gen|OldList] end, GenListAcc, GenList),
{NewGenListAcc, NewOutputList}.
gen_callback(Func, HeaderMsg) ->
"-callback "++Func++"(Msg :: "++HeaderMsg++":msg(), State :: "++HeaderMsg++":state()) -> Reply :: "++HeaderMsg++":func_reply().\n".
gen_handle_msg(_Mod, _Func, _MsgPb, _Input, "UNDEFINED", _UpperOutput) -> "";
gen_handle_msg(Mod, Func, MsgPb, Input, UpperInput, "UNDEFINED") ->
"handle_msg(?C_CMD_"++UpperInput++", Binary, State) ->
Msg = "++MsgPb++":decode_msg(Binary, "++Input++"),
"++Mod++":"++Func++"(Msg, State);\n";
gen_handle_msg(Mod, Func, MsgPb, Input, UpperInput, UpperOutput) ->
"handle_msg(?C_CMD_"++UpperInput++", Binary, State) ->
Msg = "++MsgPb++":decode_msg(Binary, "++Input++"),
Maps = "++Mod++":"++Func++"(Msg, State),
case maps:find(msg, Maps) of
{ok, RespMsg} ->
PbBinary = "++MsgPb++":encode_msg(RespMsg),
RespBinary = bg_msg:encode_msg(?THIS_CMD, ?C_CMD_"++UpperOutput++", PbBinary),
Maps#{resp_binary => RespBinary};
error -> Maps
end;\n".
gen_handle_msg_last() ->
"handle_msg(CCmd, _Binary, _State) ->
#{error => {not_defined_c_cmd, CCmd}}.".
gen_decode_input_msg() ->
"decode_input_msg(Binary) ->
{?THIS_CMD, CCmd, MsgBinary} = bg_msg:decode_msg(Binary),
decode_input(CCmd, MsgBinary).".
gen_decode_output_msg() ->
"decode_output_msg(Binary) ->
{?THIS_CMD, CCmd, MsgBinary} = bg_msg:decode_msg(Binary),
decode_output(CCmd, MsgBinary).".
gen_decode_input(_Mod, _Func, _MsgPb, _Input, "UNDEFINED") -> "";
gen_decode_input(Mod, Func, MsgPb, Input, UpperInput) ->
"decode_input(?C_CMD_"++UpperInput++", MsgBinary) ->
{"++Mod++", "++Func++", "++MsgPb++":decode_msg(MsgBinary, "++Input++")};\n".
gen_decode_input_last() ->
"decode_input(CCmd, _MsgBinary) ->
{error, {not_defined_c_cmd, CCmd}}.".
gen_decode_output(_MsgPb, _Output, "UNDEFINED") -> "";
gen_decode_output(MsgPb, Output, UpperOutput) ->
"decode_output(?C_CMD_"++UpperOutput++", MsgBinary) ->
"++MsgPb++":decode_msg(MsgBinary, "++Output++");\n".
gen_decode_output_last() ->
"decode_output(CCmd, _MsgBinary) ->
{error, {not_defined_c_cmd, CCmd}}.".
gen_encode_msg(MsgPb, HeaderMsg) ->
"encode_msg(CCmd, RespMsg) ->
RespBinary = "++MsgPb++":encode_msg(RespMsg),
"++HeaderMsg++":encode_msg(?THIS_CMD, CCmd, RespBinary).".
gen_footer() ->
"-endif.".
gen_send_msg() ->
"send_msg(undefined, _CCmd, _Msg) -> undefined;
send_msg(Pid, CCmd, Msg) ->
Binary = encode_msg(CCmd, Msg),
Pid ! {send, Binary},
ok.".